# vue3+webpack5 实践
# 搭建思路
必要配置
相关包
- vue 相关的包:vue@next,@vue/compiler-sfc,@vue/eslint-config-standard,vue-router,vue-loader
- babel 编译相关:webpack5 已不在需要
- eslint 校验相关:babel-eslint,eslint,eslint-plugin-html,eslint-plugin-import,eslint-plugin-vue
- prettier 格式化相关:eslint-plugin-prettier,eslint-config-prettier,prettier
- 环境变量控制相关:cross-env
- webpack 配置相关:webpack,webpack-cli,webpack-dev-server,webpack-merge
- css 以及样式相关:sass,node-sass,sass-loader,style-loader
配置
- 基础配置
console.log('读取公共配置') const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') const { VueLoaderPlugin } = require('vue-loader') const path = require('path') module.exports = { // 公共配置 entry: './src/index.js', output: { filename: '[name].[contenthash].bundle.js', publicPath: '/', clean: true }, plugins: [ new HtmlWebpackPlugin({ title: '商城首页', filename: 'index.html', template: './public/index.html' }), new VueLoaderPlugin(), new webpack.DefinePlugin({ 'process.env': { env: JSON.stringify(process.env) } }) ], module: { rules: [ { test: /.vue$/, use: { loader: 'vue-loader', options: { extractCSS: true } } }, { test: /.(png|svg|jpg|jpeg|gif|eot|svg|ttf|woff|woff2)$/, use: [ { loader: 'url-loader', options: { limit: 1024 * 100, name: 'assets/images/[name].[hash].[ext]', esModule: false } } ], type: 'javascript/auto' } ] }, resolve: { alias: { _utils: path.resolve('./src/utils'), _views: path.resolve('./src/views'), _assets: path.resolve('./src/assets'), _router: path.resolve('./src/router'), _store: path.resolve('./src/store'), _public: path.resolve('./public'), _api: path.resolve('./src/api') }, extensions: ['.js', '.vue', '.json'] } }开发环境配置
console.log('development 模式') const path = require('path') const { merge } = require('webpack-merge') const base = require('./webpack.config.base') const devConfig = { mode: 'development', devtool: 'cheap-module-source-map', devServer: { static: { directory: path.join(__dirname, '../dist') }, compress: true, port: 3000, historyApiFallback: true, proxy: { '/shop': { target: 'http://192.2.121.51:9136' } } }, module: { rules: [ { test: /.css$/, use: [ 'style-loader', { loader: 'css-loader', options: { url: true } } ] }, { test: /.(scss|sass)$/, use: [ 'style-loader', { loader: 'css-loader', options: { url: true } }, { loader: 'px2rem-loader', options: { remUnit: 37.5, remPrecesion: 8 } }, 'sass-loader' ] } ] } } module.exports = merge(base, { ...devConfig })生产/测试环境配置
const { merge } = require('webpack-merge') const TerserJSPlugin = require('terser-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') const base = require('./webpack.config.base') const prod = { mode: 'production', devtool: 'cheap-module-source-map', output: { filename: '[name].[contenthash].bundle.js', publicPath: '/h5/shop/dist/', clean: true }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].[hash].css', chunkFilename: '[id].css' }) ], optimization: { removeEmptyChunks: true, minimizer: [new TerserJSPlugin({}), new CssMinimizerPlugin({})] }, module: { rules: [ { test: /.css$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { url: true } } ] }, { test: /.(scss|sass)$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { url: true } }, { loader: 'px2rem-loader', options: { remUnit: 37.5, remPrecesion: 8 } }, 'sass-loader' ] } ] } } module.exports = merge(base, { ...prod })
优化打包体积
- 压缩混淆
- js 压缩混淆两个压缩文件的插件,分别对应 js 和 css 压缩
optimization: { removeEmptyChunks: true, minimizer: [new TerserJSPlugin({}), new CssMinimizerPlugin({})] } - css 压缩单独提取文件
plugins: [ new MiniCssExtractPlugin({ filename: '[name].[hash].css', chunkFilename: '[id].css' }) ]
- js 压缩混淆
- 压缩混淆
优化打包速度
- 多线程打包
使用 happyPack 做多线程打包,可以提升打包速度 - 强打包缓存
无需配置,webpack 默认强缓存,如需配置,参见:缓存配置 (opens new window)
- 多线程打包
# 新语法
- 开发常用方法
- ref 文档 (opens new window)
- reactive 文档 (opens new window)
- toRefs 文档 (opens new window)
- getCurrentInstance 文档 (opens new window)
- 生命周期钩子方法
- setup
- 参见官方文档 (opens new window)
- 参数说明:
- Props,组件的 props 值
- context 对象,暴露了以前在 this 上暴露的属性,attrs,slots,emit
- 参数不能使用...展开,如使用展开操作,会使得参数丢失响应性
# 遇到的问题
- key 值不更新问题
原因:key 值相同的情况下,vue 认为组件无需更新,无论数据是否变动,均不会重新渲染组件
解决方案:在需要重排组件内容的时候,重排时使用时间戳+id+循环序号作为 key - 双向绑定丢失响应
- 在 setup 内定义的属性,直接使用等于号赋值,无响应式
- 在 setup 内定义属性,值类型的使用 ref,赋值使用 xxx.value,即可使用响应式;高级内容,需要使用 reactive 方法包裹,return 时,如果展开需要使用 toRefs 方法包起来。
← 小程序 Lottie动画解析 →