创建项目
vue create vue-mobile-app
手动选择特性
1.Please pick a preset: 请选择一个预设
ts-pritter-es-iview (vue-router, vuex, node-sass, babel, typescript, eslint, unit-mocha)
default (babel, eslint) > Manually select features
2.Please pick a preset: Manually select features? Check the features needed for your project: 请选择一个预设:手动选择功能? 检查项目所需的功能:
(*) Babel
( ) TypeScript
(*) Progressive Web App (PWA) Support
(*) Router
(*) Vuex
(*) CSS Pre-processors
(*) Linter / Formatter
( ) Unit Testing
( ) E2E Testing
3.Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) 路由模式选择
4.Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): 选择一个CSS预处理器(默认情况下支持PostCSS,Autoprefixer和CSS模块): Sass/SCSS (with dart-sass) Sass/SCSS (with node-sass) √ Less Stylus
5.Pick a linter / formatter config: 选择一个linter / formatter配置:
ESLint with error prevention only
ESLint + Airbnb config
ESLint + Standard config √
ESLint + Prettier
配置 Eslint 检查规则
根目录新建 .eslintrc.js
// # eslint 配置项
module.exports = {
root: true,
env: {
node: true
},
extends: ['eslint:recommended', 'plugin:vue/essential', '@vue/standard'],
parserOptions: {
parser: 'babel-eslint'
},
rules: {
'no-console': 'off',
'accessor-pairs': [2, { setWithoutGet: true }], // 强制 getter 和 setter 在对象中成对出现
'arrow-spacing': [2, { before: true, after: true }], // 强制箭头函数的箭头前后使用一致的空格
'block-spacing': [2, 'always'], // 禁止或强制在代码块中开括号前和闭括号后有空格
'brace-style': [2, '1tbs', { allowSingleLine: true }], // 强制在代码块中使用一致的大括号风格
camelcase: [2, { properties: 'always' }], // 强制使用骆驼拼写法命名约定
'comma-dangle': [2, 'never'], // 要求或禁止末尾逗号
'comma-spacing': [2, { before: false, after: true }], // 强制在逗号周围使用空格
'comma-style': [2, 'last'], // 要求逗号放在数组元素、对象属性或变量声明之后,且在同一行
'constructor-super': 2, // 要求在构造函数中有 super() 的调用
curly: 2, // 要求遵循大括号约定
'dot-location': [2, 'property'], // 要求点操作符和属性放在同一行
'eol-last': 2, // 要求在非空文件末尾至少存在一行空行
eqeqeq: [2, 'always'], // 强制在任何情况下都使用 === 和 !==
'generator-star-spacing': [2, { before: false, after: true }], // 强制 generator 函数中 * 号后有空格
'handle-callback-err': [2, '^(err|error)$'], // 强制回调错误处理
indent: [2, 2, { SwitchCase: 1 }], // 强制使用一致的缩进
'jsx-quotes': [2, 'prefer-single'], // 强制在 JSX 属性中一致地使用单引号
'key-spacing': [2, { beforeColon: false, afterColon: true }], // 强制在对象字面量的属性中键和值之间使用一致的间距
'keyword-spacing': [2, { before: true, after: true }], // 关键字周围空格的一致性
'new-cap': [2, { newIsCap: true, capIsNew: false }], // 要求构造函数首字母大写
'new-parens': 2, // 要求调用无参构造函数时带括号
'no-array-constructor': 2, // 禁止使用 Array 构造函数
'no-caller': 2, // 禁用 caller 或 callee
'no-class-assign': 2, // 不允许修改类声明的变量
'no-cond-assign': 2, // 禁止条件表达式中出现赋值操作符
'no-const-assign': 2, // 禁止修改 const 声明的变量
'no-control-regex': 2, // 禁止在正则表达式中使用控制字符
'no-delete-var': 2, // 禁止删除变量
'no-dupe-args': 2, // 禁止 function 定义中出现重名参数
'no-dupe-class-members': 2, // 禁止类成员中出现重复的名称
'no-dupe-keys': 2, // 禁止对象字面量中出现重复的 key
'no-duplicate-case': 2, // 禁止出现重复的 case 标签
'no-empty-character-class': 2, // 禁止在正则表达式中使用空字符集
'no-empty-pattern': 2, // 禁止使用空解构模式
'no-eval': 2, // 禁用 eval()
'no-ex-assign': 2, // 禁止对 catch 子句的参数重新赋值
'no-extend-native': 2, // 禁止扩展原生对象
'no-extra-bind': 2, // 禁止不必要的 .bind() 调用
'no-extra-boolean-cast': 2, // 禁止不必要的布尔转换
'no-extra-parens': 2, // 禁止不必要的括号
'no-fallthrough': 2, // 禁止 case 语句落空
'no-floating-decimal': 2, // 禁止数字字面量中使用前导和末尾小数点
'no-func-assign': 2, // 禁止对 function 声明重新赋值
'no-implied-eval': 2, // 禁止使用类似 eval() 的方法
'no-inner-declarations': [2, 'both'], // 禁止在嵌套的块中出现变量声明或 function 声明
'no-invalid-regexp': 2, // 禁止 RegExp 构造函数中存在无效的正则表达式字符串
'no-irregular-whitespace': 2, // 禁止在字符串和注释之外不规则的空白
'no-iterator': 2, // 禁用 __iterator__ 属性
'no-label-var': 2, // 不允许标签与变量同名
'no-labels': [2, { allowLoop: false, allowSwitch: false }], // 禁用标签语句
'no-lone-blocks': 2, // 禁用不必要的嵌套块
'no-mixed-spaces-and-tabs': 2, // 禁止空格和 tab 的混合缩进
'no-multi-spaces': 2, // 禁止使用多个空格
'no-multi-str': 2, // 禁止使用多行字符串
'no-multiple-empty-lines': [2, { max: 1 }], // 不允许多个空行
'no-global-assign': 2, // 禁止对原生对象或只读的全局对象进行赋值
'no-unsafe-negation': 2, // 禁止在in表达式中否定左操作数
'no-new-object': 2, // 禁用 Object 的构造函数
'no-new-require': 2, // 禁止调用 require 时使用 new 操作符
'no-new-symbol': 2, // 禁止 Symbolnew 操作符和 new 一起使用
'no-new-wrappers': 2, // 禁止对 String,Number 和 Boolean 使用 new 操作符
'no-obj-calls': 2, // 禁止把全局对象作为函数调用
'no-octal': 2, // 禁用八进制字面量
'no-octal-escape': 2, // 禁止在字符串中使用八进制转义序列
'no-path-concat': 2, // 禁止对 __dirname 和 __filename 进行字符串连接path.jonin() 和 path.reslove() 非常适合替换字符串拼接来创建文件和目录路径。
'no-proto': 'error',
'no-redeclare': 2, // 禁止多次声明同一变量
'no-regex-spaces': 2, // 禁止正则表达式字面量中出现多个空格
'no-return-assign': [2, 'except-parens'], // 禁止出现赋值语句,除非使用括号把它们括起来。
'no-self-assign': 2, // 禁止自我赋值
'no-self-compare': 2, // 禁止自身比较
'no-sequences': 2, // 禁用逗号操作符
'no-shadow-restricted-names': 2, // 禁止将标识符定义为受限的名字
'func-call-spacing': [2, 'never'], // 要求或禁止在函数标识符和其调用之间有空格
'no-sparse-arrays': 2, // 禁用稀疏数组
'no-this-before-super': 2, // 禁止在构造函数中,在调用 super() 之前使用 this 或 super
'no-throw-literal': 2, // 禁止抛出异常字面量
'no-trailing-spaces': 2, // 禁用行尾空白
'no-undef': 2, // 禁用未声明的变量,除非它们在 /*global */ 注释中被提到
'no-undef-init': 2, // 禁止将变量初始化为 undefined
'no-unexpected-multiline': 2, // 禁止出现令人困惑的多行表达式
'no-unmodified-loop-condition': 2, // 禁用一成不变的循环条件
'no-unneeded-ternary': [2, { defaultAssignment: false }], // 禁止可以在有更简单的可替代的表达式时使用三元操作符
'no-unreachable': 2, // 禁止在return、throw、continue 和 break 语句之后出现不可达代码
'no-unsafe-finally': 2, // 禁止在 finally 语句块中出现控制流语句
'no-unused-vars': [2, { vars: 'all', args: 'none' }], // 禁止出现未使用过的变量,不检查参数
'no-useless-call': 2, // 禁止不必要的 .call() 和 .apply()
'no-useless-computed-key': 2, // 禁止在对象中使用不必要的计算属性
'no-useless-constructor': 2, // 禁用不必要的构造函数
'no-useless-escape': 2, // 禁用不必要的转义字符
'no-whitespace-before-property': 2, // 禁止属性前有空白
'no-with': 2, // 禁用 with 语句
'one-var': [2, { initialized: 'never' }], // 要求每个作用域的初始化的变量有多个变量声明
'operator-linebreak': [
2,
'after',
{
overrides: {
'?': 'before',
':': 'before'
}
}
], // 强制操作符使用一致的换行符风格
'padded-blocks': [2, 'never'], // 禁止块语句和类的开始或末尾有空行
quotes: [2, 'single', { avoidEscape: true, allowTemplateLiterals: true }], // 强制使用一致的反勾号、双引号或单引号
semi: [2, 'never'], // 禁止在语句末尾使用分号
'semi-spacing': [2, { before: false, after: true }], // 强制分号之前和之后使用一致的空格
'space-before-blocks': [2, 'always'], // 强制在块之前使用一致的空格
'space-before-function-paren': ['error', 'always'], // 强制在 function的左括号之前使用一致的空格
'space-in-parens': [2, 'never'], // 强制在圆括号内使用一致的空格
'space-infix-ops': 2, // 要求操作符周围有空格
'space-unary-ops': [2, { words: true, nonwords: false }], // 强制在一元操作符前后使用一致的空格
'spaced-comment': [
2,
'always',
{
markers: [
'global',
'globals',
'eslint',
'eslint-disable',
'*package',
'!',
','
]
}
], // 强制在注释中 // 或 /* 使用一致的空格
'template-curly-spacing': [2, 'never'], // 禁止花括号内出现空格
'use-isnan': 2, // 要求使用 isNaN() 检查 NaN
'valid-typeof': 2, // 强制 typeof 表达式与有效的字符串进行比较
'wrap-iife': [2, 'any'], // 要求 立即执行函数 使用括号括起来
'yield-star-spacing': [2, 'both'], // 强制在 yield* 表达式中 * 周围使用空格
yoda: [2, 'never'], // 禁止 “Yoda” 条件
'prefer-const': 2, // 要求使用 const 声明那些声明后不再被修改的变量
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'object-curly-spacing': [2, 'always', { objectsInObjects: false }], // 要求花括号内有空格 (除了 {})禁止以对象元素开始或结尾的对象的花括号中有空格
'array-bracket-spacing': [2, 'never'], // 强制数组方括号中使用一致的空格
'vue/jsx-uses-vars': 2, // 防止JSX中使用的变量被标记为未使用
'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }]
}
}
vue.config.js配置
根目录创建 vue.config.js 生成环境开启了cdn加速, TerserPlugin插件开启多线程压缩
const path = require('path')
const TerserPlugin = require('terser-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
.BundleAnalyzerPlugin
const port = 8090 // dev port
const isProduction = process.env.NODE_ENV === 'production'
function resolve (dir) {
return path.join(__dirname, dir)
}
const externals = {
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex': 'Vuex',
'vant': 'vant',
'axios': 'axios'
}
const cdn = {
css: ['https://cdn.jsdelivr.net/npm/vant@2.0.5/lib/index.css'],
js: [
'https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.1.3/vue-router.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.1/axios.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.2/vuex.min.js',
'https://cdn.jsdelivr.net/npm/vant@2.0.5/lib/vant.min.js'
]
}
module.exports = {
publicPath: './', // router hash 模式使用
outputDir: 'dist',
assetsDir: 'static',
lintOnSave: !isProduction,
parallel: require('os').cpus().length > 1, // 构建时开启多进程处理babel编译
productionSourceMap: false, // 是否为生产环境构建生成 source map
devServer: {
port: port,
open: true,
overlay: {
warnings: false,
errors: true
},
proxy: {
'/api': {
target: 'http://127.0.0.1:3000',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
},
before: app => { }
},
css: {
// https://cli.vuejs.org/zh/guide/css.html#css-modules
sourceMap: false, // 是否在构建样式地图,false将提高构建速度
loaderOptions: {
// 给 sass-loader 传递选项
sass: {
prependData: `@import "@/styles/variables.scss";` // 向所有 scss 样式传入共享的全局变量
}
}
},
// webpack 自定义配置
configureWebpack: config => {
if (isProduction) {
// 生产环境配置
Object.assign(config, {
externals: externals
})
config.plugins.push(new BundleAnalyzerPlugin())
config.plugins.push(
new TerserPlugin({
// 是否开启多线程
parallel: true,
test: /\.js(\?.*)?$/i,
terserOptions: {
// 去除打印
compress: {
'warnings': false,
'drop_console': true,
'drop_debugger': true,
'pure_funcs': ['console.log']
}
}
})
)
} else {
// 开发环境配置
}
},
chainWebpack: config => {
config.plugins.delete('preload') // TODO: need test
config.plugins.delete('prefetch') // TODO: need test
// 添加别名设置
config.resolve.alias
.set('@', resolve('src'))
.set('@api', resolve('src/api'))
.set('@store', resolve('src/store'))
.set('@views', resolve('src/views'))
.set('@utils', resolve('src/utils'))
.set('@styles', resolve('src/styles'))
.set('@mixins', resolve('src/mixins'))
.set('@assets', resolve('src/assets'))
.set('@components', resolve('src/components'))
config.when(isProduction, config => {
// 添加CDN参数到htmlWebpackPlugin配置中, 详见public/index.html
config.plugin('html').tap(args => {
args[0].cdn = cdn
return args
})
config
.plugin('ScriptExtHtmlWebpackPlugin')
.after('html')
.use('script-ext-html-webpack-plugin', [
{
// `runtime` must same as runtimeChunk name. default is `runtime`
inline: /runtime\..*\.js$/
}
])
.end()
config.optimization.splitChunks({
chunks: 'all',
cacheGroups: {
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial' // only package third parties that are initially dependent
},
vant: {
name: 'chunk-vant', // split vant into a single package
priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
test: /[\\/]node_modules[\\/]_?vant(.*)/ // in order to adapt to cnpm
},
commons: {
name: 'chunk-commons',
test: resolve('src/components'), // can customize your rules
minChunks: 3, // minimum common number
priority: 5,
reuseExistingChunk: true
}
}
})
config.optimization.runtimeChunk('single')
})
}
}
移动端自适应配置
安装依赖:npm install postcss-aspect-ratio-mini postcss-cssnext postcss-px-to-viewport postcss-viewport-units postcss-write-svg cssnano cssnano-preset-advanced -D
新建 postcss.config.js 更多配置参考 github.com/michael-cin…
module.exports = ({ file, options, env }) => {
const isVant = !!(file.dirname.includes('node_modules') && file.dirname.includes('vant'))
const vWidth = isVant ? 375 : 750
const vHeight = isVant ? 667 : 1334
const viewportUnitsConfig = {
'postcss-viewport-units': {}
}
const pluginsConfig = {
plugins: {
'postcss-aspect-ratio-mini': {},
'postcss-write-svg': { utf8: false }, // 处理移动端1px的问题
'postcss-cssnext': {}, // 让我们可以使用css未来特性 会对这些特性做相关的兼容性处理
'postcss-px-to-viewport': {
viewportWidth: vWidth, // 视窗的宽度,对应的是我们设计稿的宽度,一般是750
viewportHeight: vHeight, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置
unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除)
viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw
selectorBlackList: ['.ignore', '.hairlines'], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名
minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值
mediaQuery: false // 允许在媒体查询中转换`px`
},
'cssnano': { // cssnano主要用来压缩和清理CSS代码。在Webpack中,cssnano和css-loader捆绑在一起,所以不需要自己加载它
preset: 'advanced', // 安装cssnano-preset-advanced
autoprefixer: false, // 重复调用 禁用autoprefixer
'postcss-zindex': false // 禁用postcss-zindex 否则z-index 的值会被重置为1
}
}
}
if (isVant) {
return pluginsConfig
} else {
return Object.assign(pluginsConfig, viewportUnitsConfig)
}
}