• 从入门到入土的Vue2+webpack


    前言:本文只是根据自建的项目所做的文档,文中没有面面俱到,一些小配置并没有在上面写,在实际开发中可能会有写不可预估的出入,希望对各位看官有一定的参考价值

    引子:目前社区比较知名的脚手架,有vue-cli、create-react-app,他们都解决了一站式的项目环境配置问题虽然。不过还有一些问题,它们没法解决,比如:1、vue-cli专注的vue项目的环境搭建和配置问题、那每个公司的业务类型不一样,有些公司是金融业务,有些公司是物流业务,如何把相关的东西集成进来?2、每一个公司在项目的沉淀中都是不一样的,比如组件的沉淀、监控埋点方案的沉淀、以及其他样式、目录、service等等一系列编码相关的沉淀,用什么样的方式,能够避免我们的重复劳动? vue-cli 已经非常成熟,成熟到可能自己写的 webpack 性能上不一定比得上vue-cli。当然只是性能上。在实用性,拓展性,可玩性却有很大的操作空间,vue-cli是把饭喂到嘴边,但是并不知道这个饭是如何做的,只见其名不知其意,相比于自己搭建一套环境,你可以知道各个版本的差异化,哪些配置已经废除或者有更好的替代品,不同版本间的环境依赖如何做兼容,哪些方式是最佳实践,对基础业务有更深的理解

    关于项目根文件下package.json中的devDependencies–save-d和dependencies–save常见认知:devDependencies用于本地环境开发。dependencies用于生产环境。通过NODE_ENV=developementNODE_ENV=production指定开发或生产环境。更多参考

    关于全局变量process.env.NODE_ENV,在webpack5中可以使用new webpack.DefinePlugin({})来定义一个或者多个不同的全局变量(不是windows),里面的值需要JSON.stringify包一层。优先级DefinePlugin > --mode(package.json) > mode(webpack.dev或者webpack.prod中定义的)。更多请戳关于环境变量中的命名规则可以借鉴模式与环境变量 1.初始化:npm init (或者使用默认执行的命令npm init -y) 根目录创建build——webpack.dev.js,webpack.pord.js,webpack.base.js 根目录创建index.html,也可以在public下创建 根目录创建src——App.vue,main.js

    2.安装webpack: npm i webpack webpack-cli -D (这里默认是最新版本,如果想使用旧版本的话可以用npm i --save-dev webpack@。不建议全局安装,会将项目中的webpack锁定到指定版本,在使用不同版本的项目中可能会导致构建失败。关于为什么要单独安装webpack-cli——已经从webpack4后版本剥离)

    3.安装vue及相关loader: npm i vue@2 -S,npm i vue-loader -D(解析和转换vue文件,交给对应的loader处理),npm i vue-template-compiler -D(编译vue模板的包,传入模板返回AST抽象语法树。vue3替代为@vue/compiler-sfc)

    4.样式相关:npm install -D style-loader css-loader postcss-loader autoprefixer sass-loader sass-resources-loader npm install --save postcss npm install less less-loader --save (采用sass与less可供选择。style-loadervue-style-loader之间推荐使用后者)

    5.html相关:npm i html-webpack-plugin -D(在打包结束后,自动生成一个html文件,并把打包生成的js文件引入到这个html文件当中)

    6.webpack相关配置:npm install webpack-dev-server -D(对代码进行热重载)。npm install -D cross-env(兼容不同平台)。npm i clean-webpack-plugin -D(在打包之前清空output配置的文件夹)

    7.文件类配置:npm install file-loader url-loader -D

    {test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,type: 'asset',parser: {dataUrlCondition: {maxSize: 10 * 1024, // 默认是 8kb},},generator: {filename: 'static/images/[name][hash:5][ext]',},exclude: /node_modules/,},{test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,type: 'asset/resource', // 类似 file-loader 导出文件generator: {filename: 'static/media/[name][hash:5][ext]',},exclude: /node_modules/,},// 字体文件{test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,type: 'asset/resource',generator: {filename: 'static/font/[name][hash:5][ext]',},exclude: /node_modules/,} 
    
    • 1

    8.配置文件哈希值:webpack 提供了三种 hash 方式,分别是 hash,chunkhash,contenthash。

    ①hash 是项目工程级的,整个工程构建的文件 hash都是一样的,所以只要工程文件有一个修改了,那么所有打包文件的,hash都会改变,这明显不利于文件缓存,比如第三方库的 chunk 
    
    • 1
    ②chunkhash 和hash不一样,它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的hash值。我们在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,接着我们采用chunkhash的方式生成hash值,那么只要我们不改动公共库的代码,就可以保证其hash值不会受影响 
    
    • 1
    ③contenthash 表示由文件内容产生的hash值,内容不同产生的 contenthash值也不一样。在项目中,通常做法是把项目中 css 都抽离出对应的 css 文件来加以引用 
    
    • 1

    配置文件更改:filename:'[name].[contenthash].js'

    9.区分运行环境:运行npm run serve后, dist目录被清空了。这并不是我们想看到的,加上还有非常多场景需要我们区分开发和生产环境,比如配置 sourceMap 之类的问题。所以在build文件下再新建webpack.pord.js文件(生产环境),安装npm i webpack-merge -D用来合并配置

    // 生产环境
    const devConfig = require('./webpack.dev')
    const { merge } = require('webpack-merge')
    const { CleanWebpackPlugin } = require('clean-webpack-plugin')
    module.exports = merge(devConfig, {mode: 'production',plugins: [new CleanWebpackPlugin()]
    }) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    10.针对浏览器配置css前缀自动补全:npm i autoprefixer postcss-loader --save.注意:配置 Autoprefixer 之前,需要先添加Browserslist。可以在根目录一个.browserslistrc文件,也可以在package.json 文件中添加:browserslist。不过一定要加,不然 autoprefixer不生效。其次,必须在根目录创建一个 postcss.config.js

    package.json:

    "browserslist": ["last 1 chrome version","last 1 firefox version","last 1 safari version", "> 1%", "iOS >= 7", "Android > 4.1"] 
    
    • 1

    11.配置CSS样式分离:npm i mini-css-extract-plugin --save. 建议先看这篇文章webpack/vue-cli中的 publicPath 区别 loader改成miniCssExtractPlugin后就不能实现css的热加载 不过开发时也不需要分离css,在生产环境使用即可 webpack.prod.js:

    ...
    const MiniCssExtractPlugin = require('mini-css-extract-plugin')
    rules:[
    {test: /\.(less|css)$/,use: [{loader: MiniCssExtractPlugin.loader,options: {publicPath: '../' // 注意添加了输出路径}},{ loader: 'css-loader', options: { esModule: false } },'postcss-loader', 'less-loader']},
    ]plugins: [new MiniCssExtractPlugin({// css的分离到了单独的 css 目录下,并且哈希值继续使用 contenthashfilename: 'css/[name].[contenthash:4].css'})] 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    12.ES6转ES5:npm i babel-loader @babel/preset-env @babel/core --save npm install --save core-js@3 webpack.dev.js:

    rules: [{test: /\.js$/,use: ['babel-loader'],include: path.resolve(__dirname, '../src'),exclude: /(node_modules)/},] 
    
    • 1

    在根目录新增 .babelrc 文件:

    {"presets": [["@babel/preset-env",{"useBuiltIns": "usage","corejs": 3}]]
    } 
    
    • 1
    • 2

    另一个方案是 @babel/plugin-transform-runtime + @babel/runtime + @babel/runtime-corejs3

    // .babelrc
    {"plugins": ["@babel/plugin-transform-runtime",{corejs: 3}]
    } 
    
    • 1
    • 2
    • 3

    13.关于项目热更新:较为复杂的方法为依赖于express平台去使用npm i express webpack-hot-middleware webpack-dev-middleware --save.目前不做详细介绍,坑有些多。有个简单的方法可以满足日常开发需要

    plugins:[...new webpack.HotModuleReplacementPlugin(),// 当开启 HMR 的时候使用该插件会显示模块的相对路径,建议用于开发环境new webpack.NoEmitOnErrorsPlugin()
    ]
    devServer: {...hot: true,//是否使用 HMRopen: true,compress: true,//是否开启 gzipproxy: { //代理'/api/': {target: 'http://www.baidu.com',changeOrigin: true}}
    }, 
    
    • 1
    • 2
    • 3
    • 4

    14.生产环境使用splitChunks拆分代码:

    optimization: {splitChunks: {cacheGroups: {vendor: {priority: 1, // 优先级配置,优先匹配优先级更高的规则,不设置的规则优先级默认为0test: /node_modules/, // 匹配对应文件chunks: 'initial',name: 'vendor',minChunks: 1},commons: {priority: 0,chunks: 'initial',name: 'commons', // 打包后的文件名minChunks: 2,}}}},--------------------------------------------------------------------------如果使用了 html-webpack-plugin,需要添加 chunks 配置来自动引入拆分出的 chunknew HtmlWebpackPlugin({// ...chunks: ['moment','main'],
    }), 
    
    • 1
    • 2
    好了,到现在为止的一些配置可以使项目成功的运行和打包了,接下来要创建和安装一些常规的文件夹和开发中必不可少的工具:

    1.在src文件夹下新建 router,store,views,utils,styles,components,并安装vuex,vue-router,axios:npm i vuex --save,npm i vue-router --save,npm i axios --save 路由文件要使用按需加载需安装npm install@babel/plugin-syntax-dynamic-import --save babelrc文件中配置:

    {"plugins": ["@babel/plugin-syntax-dynamic-import"]
    } 
    
    • 1
    • 2

    2.安装eslint:npm install -D eslint eslint-loader @babel/eslint-parser eslint-plugin-vue 在项目根目录新增 .eslintrc.js 配置文件:

    module.exports = {root: true,globals: {process: true},parserOptions: {parser: 'babel-eslint',sourceType: 'module',ecmaFeatures: {// 支持装饰器legacyDecorators: true}},env: {browser: true,node: true,es6: true},extends: ['plugin:vue/recommended', 'eslint:recommended'],plugins: ['babel', 'prettier'],rules: {// 使用2个空格缩进indent: ['error',2,{SwitchCase: 1,flatTernaryExpressions: true}],// switch必须提供 default'default-case': 'error',// 禁止一成不变的循环,防止代码出现死循环'no-unmodified-loop-condition': 'error',// 禁止在变量未声明之前使用'no-use-before-define': 'error',// 代码后不使用分号semi: ['error', 'never'],// 注释 // 或 /* 之后必须有一个空格'spaced-comment': ['error', 'always'],// 禁止重复导入模块,对于同一模块内内容,应一次导入'no-duplicate-imports': 'error',// 必须使用let 或 const, 不能使用var'no-var': 'error',// 要求大括号内必须有空格'object-curly-spacing': ['error', 'always'],// 数组前后不需要添加空格'array-bracket-spacing': ['error', 'never'],// 箭头函数前后必须要有空格'arrow-spacing': ['error',{before: true,after: true}],// 代码中可出现console'no-console': 'off',// 正则中可以出现控制字符'no-control-regex': 'off','no-unused-vars': ['error',{ignoreRestSiblings: true,// 可以声明未使用的h,方便jsxargsIgnorePattern: 'h'}],// 行注释必须在行上面'line-comment-position': ['error', { position: 'above' }],// 组件名称必须是大驼峰'vue/name-property-casing': ['error', 'PascalCase'],// vue Html元素单标签关闭方式'vue/html-self-closing': ['error',{html: { normal: 'never', void: 'always' },svg: 'always',math: 'always'}],// 组件在template内必须使用 kebab-case 格式'vue/component-name-in-template-casing': ['error','kebab-case',{registeredComponentsOnly: false,ignores: []}],// template 内必须使用 ==='vue/eqeqeq': 'error',// 允许使用v-html'vue/no-v-html': 0,// 禁用隐式的eval() 比如 setTimeout('alert();', 100)'no-implied-eval': 'error'},
    } 
    
    • 1
    • 2

    .eslintignore 文件可以忽略那些不用语法检查的文件:

    node_modules
    /dist 
    
    • 1
    • 2

    3.安装element-ui按照官网走便是,值得一提的是引入样式文件可能会与dev文件配置的属性有冲突,把exclude: /(node_modules)/删掉即可。另个是关于官网上写的babel转es2015的问题,可以将 "presets": [["es2015", { "modules": false }]]中的es2015换成已经配置的@babel/preset-env。全部引入和按需引入只能存在一个

    优化相关
    resolve: {alias: { //配置别名可以加快搜索速度'@': path.resolve(__dirname, '../src'),},extensions: ['.js', '.jsx', '.ts', '.tsx', '.json', '.vue', '.sass', '.less'],//指定 extensions 之后,使用require 和 import 的时候就不需要加文件扩展名了,查找的时候会依次匹配,但同一个目录有不同类型的同名文件时,也只会匹配第一个modules: [ //指定第三方依赖的存放目录,默认为'node_modules'path.resolve(__dirname, '../node_modules'),'node_modules']}, 
    
    • 1
    缓存
    1. //webpack.dev.js,缓存之前打包的内容,配置之后会生成一个.cash文件夹,通过文件缓存,直接缓存到本机磁盘
    cache: {type: 'filesystem', //默认缓存到 node_modules/.cache/webpack。还可以使用 cacheDirectory选项自定义配置
    },
    2.babel-loader 自带缓存配置。开启后会将缓存放在node_modules/.cache/babel-loader
    {loader: 'babel-loader',options: {cacheDirectory: true,}
    }
    3.使用此npm i cache-loader -D,可以将其他 loader 的结果缓存到磁盘中,默认路径node_modules/.cache/cache-loader
    {test: /\.css$/,use: ['cache-loader','style-loader','css-loader'],
    }, 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    JS 压缩
    npm i terser-webpack-plugin -D
    
    // webpack.prod.js
    const TerserPlugin = require("terser-webpack-plugin");
    
    module.exports = {optimization: {minimize: true,minimizer: [new TerserPlugin({ // 压缩JS代码terserOptions: {compress: {drop_console: true, // 去除console},},})],},
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    HTML 压缩
    1.html-webpack-plugin的 minify 选项用于设置html文件的压缩
     minify: {removeComments: true,collapseWhitespace: true,removeRedundantAttributes: true,useShortDoctype: true,removeEmptyAttributes: true,removeStyleLinkTypeAttributes: true,keepClosingSlash: true,minifyJS: true,minifyCSS: true,minifyURLs: true,},
    2.使用 html-minifier-terser,webpack5 开始,像压缩类的插件,应该配置在 optimization.minimizer 数组中,方便统一管理
    npm i html-minimizer-webpack-plugin -D
    
    // webpack.prod.js
    const HtmlMinimizerPlugin = require("html-minimizer-webpack-plugin");
    
    optimization: {minimize: true,minimizer: [....new HtmlMinimizerPlugin(),],
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    CSS 压缩
    npm i css-minimizer-webpack-plugin -D
    
    // webpack.prod.js
    const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
    
    module.exports = {optimization: {// 告知 webpack 使用 TerserPlugin 或其它在 optimization.minimizer 定义的插件压缩 bundle。minimizer: [...new CssMinimizerPlugin(),],},
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    构建时间优化`thread-loader`:多进程打包,可以大大提高构建的速度,使用方法是将thread-loader放在比较费时间的loader之前,比如babel-loader
    
    npm i thread-loader -D
    
    {test: /\.js$/,use: ['thread-loader','babel-loader'],}
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    exclude & include
    
    exclude:不需要处理的文件
    include:需要处理的文件{test: /\.js$/,//使用include来指定编译文件夹include: path.resolve(__dirname, '../src'),//使用exclude排除指定文件夹exclude: /node_modules/,use: ['babel-loader']}, 
    
    • 1
    • 2
    • 3
    • 4
    source-map 产生的文件映射了压缩后的代码所对应的转换前的源代码位置,解决了代码压缩后难以调试的问题
    
    // webpack.dev.js开发环境
    
    module.exports = {mode: 'development',devtool: 'eval-cheap-module-source-map'
    }
    
    // webpack.prod.js生产环境
    
    module.exports = {mode: 'production',devtool: 'nosources-source-map'
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    压缩打包的文件
    npm i compression-webpack-plugin -D
    // webpack.prod.js
    const CompressionPlugin = require('compression-webpack-plugin')
    new CompressionPlugin({algorithm: 'gzip',threshold: 10240,test: /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i,minRatio: 0.8,deleteOriginalAssets: true }) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    打包体积分析
    npm i webpack-bundle-analyzer -D
    
    // webpack.prod.js
    
    const { BundleAnalyzerPlugin} = require('webpack-bundle-analyzer')plugins: [new BundleAnalyzerPlugin(),
    ] 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    小图片转base64
    webpack5中url-loader已被废弃,改用asset-module
     {test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,type: 'asset',parser: {dataUrlCondition: {maxSize: 25 * 1024, // 默认是 8kb},},generator: {filename: 'static/images/[name][hash:5][ext]',},exclude: /node_modules/,}, 
    
    • 1
    • 2
    • 3
    构建进度条
    npm i progress-bar-webpack-plugin -D 或者npm i webpackbar -D
    
    // webpack.base.js
    const ProgressBarPlugin = require('progress-bar-webpack-plugin')
    plugins: [...new ProgressBarPlugin() 或 new WebpackBar(),], 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    自适应布局解决方案
    npm install postcss postcss-pxtorem@5.1.1 --save-dev (vue2项目不宜装太高版本)
    npm i lib-flexible-computer --save-dev
    这里要说明lib-flexible-computer这个依赖,大部分pc的适配都是使用的npm i lib-flexible -S,这个依赖,做适配的话只能做到屏幕540一下的,pc端使用并不是很好用
    在util文件夹下创建rem.js
    (function flexible (window, document) {var docEl = document.documentElementvar dpr = window.devicePixelRatio || 1function setBodyFontSize () {if (document.body) {document.body.style.fontSize = 12 * dpr + 'px'} else {document.addEventListener('DOMContentLoaded', setBodyFontSize)}}setBodyFontSize()function setRemUnit () {var rem = docEl.clientWidth / 10docEl.style.fontSize = rem + 'px'}setRemUnit()window.addEventListener('resize', setRemUnit)window.addEventListener('pageshow', function (e) {if (e.persisted) {setRemUnit()}})if (dpr >= 2) {var fakeBody = document.createElement('body')var testElement = document.createElement('div')testElement.style.border = '.5px solid transparent'fakeBody.appendChild(testElement)docEl.appendChild(fakeBody)if (testElement.offsetHeight === 1) {docEl.classList.add('hairlines')}docEl.removeChild(fakeBody)}
    })(window, document)
    
    main.js中引入:import "lib-flexible-computer";import '@/utils/rem'
    postcss.config.js中配置:
    module.exports = {plugins: {'autoprefixer': require('autoprefixer'),'postcss-pxtorem': {rootValue: 192,propList: ['*']}} 
    }
    package.json中添加:
    "postcss": {"plugins": {"autoprefixer": {"overrideBrowserslist": ["Android 4.1","iOS 7.1","Chrome > 31","ff > 31","ie >= 8"]},"postcss-pxtorem": {"rootValue": 192,"propList": ["*"]}}
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    ==提示==:1.vue2不要安装最新的vue-loader,15即可

    2.关于webpack-dev-server中的devServer配置,在5版本中弃用了一些属性,比如说contentBase:""更改为static:{},图中是新属性供参考

    3.css热更新不生效:①.webpack内解决:css-loader 4.0 后默认对 esModule 设置的是true,而 vue-style-loader4.1.0默认接收的是commonjs的结果,也就是默认接收的是“css-loaderesModule 设置的是false的结果”,导致样式无法加载 vue-cli内解决:[cli.vuejs.org/zh/config/#…](https://link.juejin.cn/?target=https%3A%2F%2Fcli.vuejs.org%2Fzh%2Fconfig%2F%23css-extract “https://cli.vuejs.org/zh/config/#css-extract”” style=“margin: auto” />

    4.关于autoprefixer可能要进行降级处理

    5.使用webpack-merge后,两个环境的配置不要相同

    6.在vue2中使用vuex与vue-router不要默认安装最新版本,应使用3版本。路由模式history开启需要后端支持,前端独自开启会显示页面不存在

    7.如果想使用@导入组件显示路径的话,可以如下配置:打开文件 - 首选项 - 设置 - 搜索 Path Intellisense - 打开 settings.json ,添加:

    "path-intellisense.mappings": { "@": "${workspaceRoot}/src"
     }
     在vue项目 package.json 所在同级目录下创建文件 jsconfig.json:
     {"compilerOptions": {"target": "ES6","module": "commonjs","allowSyntheticDefaultImports": true,"baseUrl": "./","paths": {"@/*": ["src/*"]}},"exclude": ["node_modules"]
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    8.sass变量导出undefined问题记录

    主要是在css-loader版本较新的情况下才会需要以.module.scss结尾
    {test: /\.s[ac]ss$/i,use: ["style-loader",{loader: "css-loader",options: {modules: {namedExport: false,},},},'sass-loader']
    } 
    
    • 1
    • 2
    • 3
  • 相关阅读:
    【刷题记录12】Java工程师丨面试必会进程线程问答
    MySQL(4)
    算法: 对位置变换的字符串分组 49. Group Anagrams
    C++复习 ——内联函数
    The 2022 ICPC Asia Regionals Online Contest (I)-D题题解(DFS暴搜+剪枝+打表去重+二分)
    python3-GUI概述及应用
    基于文化优化算法图像量化(Matlab代码实现)
    微信小程序框架---详细教程
    Kubernetes审计版本如何确定?
    SpringCloud微服务之基于zuul搭建服务网关
  • 原文地址:https://blog.csdn.net/qq_53225741/article/details/126541675