create mutiple page by vue-cli4.5.15
多应用,看这个工程就够了,开箱即用。
本工程是基于@vue/cli4.5.15版本搭建的多页面应用,主要是将单页面应用改造成多页面(模块)应用,使其一个脚手架可以开发多个互不影响的模块,同时又可以共用一些公共组件和方法,非常适合公司功能非常多的应用。
git clone git@github.com:LewisLen/vue-multiple-h5.git npm install # 这里是以逗号或者分隔模块名称 npm run serve -- --module vip,page npm run dev -- --module vip_page # 模块打包 npm run build -- --module vip,page npm run build -- --module vip_page # 不同环境 npm run build:uat -- --module vip_page # 全量本地启动即devServe npm run serve # 全量打包 npm run build
构建多模块应用的关键就是module.exports中的pages关键词,最终需要把pages变成下述格式
pages:{ vip: { entry: './src/modules/vip/main.js', template: './public/index.html', filename: 'vip.html' }, page: { entry: './src/modules/page/main.js', template: './public/index.html', filename: 'page.html' } }
执行几个模块编译需要借助argv获取命令行中的参数,通过--后边可以添加参数,本工程是以下划线(_)或者逗号(,)进行分割,可以指定多个部分模块进行编译打包。
也可以使用 minimist 模块来获取 argv 字段
采用的的lib-flexible方案,搭配postcss-pxtorem插件可以直接在开发过程中用px做单位,插件会根据配置自动转化成rem,就可以直接在项目中写px单位,需要注意的是,本工程默认设计稿为 750px。
需要在 main.js 中引入 lib-flexible.js 或者直接在模板的 html 文件中引入该方案
npm install lib-flexible --save npm install postcss-pxtorem --save-dev
//mian.js import 'lib-flexible/flexible.js' // .postcssrc.js文件 module.exports = { plugins: { "autoprefixer": {}, "postcss-pxtorem": { "rootValue": 75, // 设计稿宽度的1/10 "unitPrecision": 4, // 小数位 "minPixelValue": 2, // 转换的最小单位 "selectorBlackList": [], // 忽略的样式, 正则 "propList": ["*"], // 需要做转化处理的属性,如`hight`、`margin`等,也可以正则匹配 "exclude": /node_modules/ } } }
出现报错: Syntax Error: Error: PostCSS plugin postcss-pxtorem requires PostCSS 8. 解决方案,安装postcss-pxtorem@5.1.1版本: npm install postcss-pxtorem@5.1.1 -D
安装单位转换插件
# 自动转换px单位
npm install postcss-px-to-viewport -D新增 postcss 相关配置
// .postcssrc.js module.exports = { plugins: { autoprefixer: { overrideBrowserslist: ['Android 4.1', 'iOS 7.1', 'Chrome > 31', 'ff > 31', 'ie >= 8'] }, 'postcss-px-to-viewport': { viewportWidth: 375, // 视窗的宽度,对应的是我们设计稿的宽度,一般是750 unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除) viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw selectorBlackList: ['.ignore'], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名 minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值 mediaQuery: false // 允许在媒体查询中转换`px` } } }
脚手架采用的是方案2,方案2可能存在一些兼容性问题,特别是一些低版本手机浏览器。方案1和方案2不要混合使用。
安装axios和qs,利用 interceptors 拦截器对 axios 请求进行封装
// 引用方式一 main.js import request from "@/request"; Vue.prototype.$http = request;// 挂载到原型对象上 // 组件中 this.$http.get("api/productList").then((res) => { console.log(res); }); // 引用方式二:每个请求按模块划分 // product.js import request from "../request"; import QS from "qs"; export function getProductList(data) { return request({ url: "api/productList", method: "get", // 默认是get data: QS.stringify(data), }); }
取消请求方式
// 方式一 const cancelToken = axios.CancelToken; const source = CancelToken.source(); axios.post('/productList',{code:'0001'},{ cancelToken: source.token }) source.cancel('取消请求') // 方式二 const cancelToken = axios.CancelToken; let cancel; axios.get('/productList',{ cancelToken: new cancelToken(function executor(c){ cancel = c; }) }) cancel();// 取消请求
路由懒加载
// 路由懒加载 const Task = () => import("../views/Task.vue");// ES6方式 const routes = [ { path: "/task", name: "Task", component: (resolve) => require(["../views/Task.vue"], resolve), meta: { title: "任务", keepAlive: false, // 是否需要缓存 auth: false, // 用户权限 }, } ]
路由守卫
router.beforeEach((to,from,next) => { // 路由拦截 if(!to.meta.auth){ console.log('无权限') } // 设置页面标题 if(to.meta.title){ document.title = to.meta.title; } next(); })
也可以通过 webpack 中的 require.context(要搜索的目录,是否要搜索子目录,匹配文件的正则表达式) 函数自动注册匹配路由。
// 自动注册路由 let tempRouters = []; const oFiles = require.context("../views", true, /\.vue$/); oFiles.keys().forEach((element) => { // element: ./Task.vue let componentName = element.replace(/^\.\//, "").replace(/\.vue$/, ""); tempRouters.push({ path: "/" + componentName.toLowerCase(), component: () => import("../views/" + componentName), }); });
externals 引入方式的作用需谨慎评估,确实能够减少编译包大小,但是需要额外在html文件上添加script标签以引入js文件,这就意味着需要额外的http请求以及更多的解析时间。
// vue.config.js configureWebpack: config => { config.externals = { // 依赖包名称:赋值给widnow的全局变量名称 vue: 'Vue', 'vue-router': 'vueRouter' } }
这个时候就可以手动在html模版文件中手动引入依赖包的 cdn 链接,需注意引入依赖之间以及和编译打包后的包先后顺序关系。引入的前后顺序没问题,则可以把 main.js 中 import 引入的依赖语句删除。
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
如果有很多个 cdn 链接需要引入,则可以借助 html-webpack-plugin 插件进行插入
// vue.config.js const externalsList = { css:[ "https://cdn.jsdelivr.net/npm/reset.css" ], js:[ "https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js" ] } module.exports = { configureWebpack: config => { config.externals = { // 依赖包名称:赋值给widnow的全局变量名称 vue: 'Vue', 'vue-router': 'vueRouter' } }, chainWebpack: config => { config .plugin('html') .tap(args => { args[0].cdn = externalsList return args }) } }
在 html 模版中引入,可以使用vue inspect --plugin html命令来审批配置的一部分
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %> <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" /> <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" /> <% } %> <!-- 使用CDN加速的JS文件,配置在vue.config.js下 --> <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %> <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script> <% } %>
直接安装对应的loader即可
# sass
npm install -D sass-loader@10.1.1 sass配置可以使用全局变量
css: { sourceMap: false, loaderOptions: { scss: { // 全局都可以使用的样式 prependData: `@import "@/assets/style/variables.scss";`, }, }, },
需注意 sass-loader 版本问题,用最新版有可能会出现编译报错的问题,最好是使用 sass-loader@10.1.1 版本 注意
@import "@/assets/css/variables.scss";后边需要加;
vue-cli4 自带有去除console的插件 terser-webpack-plugin 所以直接使用即可
if (process.env.NODE_ENV === "production") { config.optimization.minimizer("terser").tap((options) => { options[0].terserOptions.compress.drop_console = true; return options; }); }
为了规范git提交commit信息,需要借助husky、lint-staged和commitlint工具。husky是一款Git Hook工具,可以在 git 的各个阶段(pre-commit、commit-msg、pre-push 等)触发我们的命令。而 lint-staged 的作用就是只校验 git 提交到暂存区的文件,而避免校验全部文件。
npx husky-init && npm install # npm方式 npx husky-init && yarn # Yarn 1方式 yarn dlx husky-init --yarn2 && yarn # Yarn 2方式
执行命令会新增husky依赖,在根目录下创建.husky目录并且会生成初始化pre-commit
"scripts":{ "prepare":"husky install" }
生成初始化的.husky/pre-commit文件
#!/bin/sh . "$(dirname "0ドル")/_/husky.sh" npx test # 改为以下内容,则会在commit时执行以下命令,会校验所有js和vue文件 eslint --fix .src --ext .vue,.js.ts
此时需要借助lint-staged
npm install lint-staged -D
更改package.json配置信息
"lint-staged": { "src/**/*.{js,json,vue,ts,tsx}": "eslint --fix" }
.husky/pre-commit文件
#!/bin/sh . "$(dirname "0ドル")/_/husky.sh" npx lint-staged
至此,在每次commit的时候,就会校验add文件是否符合eslint规范,如不符合则不允许提交
# @commitlint/cli为git commit校验 # @commitlint/config-conventional规范也可以换成@commitlint/config-angular npm install @commitlint/config-conventional @commitlint/cli -D
创建commitlint.config.js并配置信息
module.exports = { extends: ["@commitlint/config-conventional"] // 也可以换成["@commitlint/config-angular"]标准 };
在.husky目录下创建commit-msg文件验证commit message提交信息
npx husky add .husky/commit-msg "npx --no-install commitlint --edit 1ドル"至此,在每次commit的时候,就会先校验add文件是否符合eslint规范,如不符合则不允许提交。然后再校验commit message是否符合规范,必须是以下几种类型。
- build:编译相关的修改,例如发布版本、对项目构建或者依赖的改动
- chore: 改变构建流程、或者增加依赖库、工具等
- ci: 持续集成工具类改动
- docs: 文档改动
- feat: 新功能(业务)模块,新特性
- fix: 修复bug
- perf: 优化相关,比如提升性能、体验
- refactor: 重构(即不是新增功能,也不是修改bug的代码变动)
- revert: 回滚代码
- style: UI走查,css样式改动
- test: 测试用例,包括单元测试、集成测试等
如果是 vue-cli 改造,则有可能会有冲突,因为在安装之后,@vue/cli-service 也会安装 yorkie,且yorkie fork 自 husky 不兼容。如果还有问题,可以尝试删除 .git/hooks/ 下面的 pre-commit 和 commit-msg 文件再试试,按照 husky 官网再试试。
"lint-staged": { "src/**/*.{js,json,ts,tsx}": [ "prettier --write", "eslint --fix" ], "src/**/*.{html,css,scss,sass}": [ "stylelint --fix" ], "src/**/*.vue": [ "prettier --write", "eslint --fix", "stylelint --fix" ] }
如果有
.DS_Store文件,可以执行sudo find . -name "*.DS_Store" -type f -delete命令来删除
pre-commit不生效的问题
查看.git/config文件中的hooksPath是什么,如果是要用husky,则需要配置为hooksPath=.husky,如果用默认的,则删除即可。
mac有可能没有权限执行pre-commit和commit-message脚本,需要执行chomd 777 .git/hooks/pre-commit和chomd 777 .git/hooks/commit-message赋权限
安装相关依赖,特别需要注意版本,我在这个lint浪费了不少时间,最后发现都是插件版本的问题。
vscode extension 的 stylelint 版本建议是0.87.6,依赖建议如下版本:
"stylelint": "^13.13.1", "stylelint-config-prettier": "^9.0.3", "stylelint-config-standard": "^22.0.0", "stylelint-loader": "^5.0.0", "stylelint-scss": "^4.1.0",
添加.stylelintrc.js文件,并且做规则限制
module.exports = { extends: [ 'stylelint-config-standard', 'stylelint-config-prettier', ], plugins: ['stylelint-order','stylelint-scss'], ignoreFiles: ["./README.md","./src/assets/style/reset.css"], rules: { "selector-class-pattern": [ // 规范class类名.box-element-modifier "^[a-z]([a-z0-9]){1,8}(-[a-z0-9]+)?((-|--)[a-z0-9]+)?$", { "resolveNestedSelectors": true, "message":"类名格式不对", } ], // ... } };