webpack5+vue3手动搭建

201 阅读3分钟

搭建思路

  1. 必要配置
    1. 相关包

      1. vue相关的包:vue@next,@vue/compiler-sfc,@vue/eslint-config-standard,vue-router,vue-loader
      2. babel编译相关:webpack5已不在需要
      3. eslint校验相关:babel-eslint,eslint,eslint-plugin-html,eslint-plugin-import,eslint-plugin-vue
      4. prettier格式化相关:eslint-plugin-prettier,eslint-config-prettier,prettier
      5. 环境变量控制相关:cross-env
      6. webpack配置相关:webpack,webpack-cli,webpack-dev-server,webpack-merge
      7. css以及样式相关:sass,node-sass,sass-loader,style-loader
    2. 配置

      1. 基础配置
           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']
             }
           }
      
      1. 开发环境配置
          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_goods': {
                  target: 'http://10.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 }) 
        
      2. 生产/测试环境配置
          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: '//wximg.91160.com/h5/shopn/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 })
        
        
  2. 优化打包体积
    1. 压缩混淆
      1. js压缩混淆
        optimization: {
           removeEmptyChunks: true, 
           minimizer: [new TerserJSPlugin({}), new CssMinimizerPlugin({})] 
        }
        
        两个压缩文件的插件,分别对应js和css压缩
      2. css压缩单独提取文件
        plugins: [
            new MiniCssExtractPlugin({ 
                filename: '[name].[hash].css',
                chunkFilename: '[id].css'
            })
        ]
        
  3. 优化打包速度
    1. 多线程打包
      使用happyPack做多线程打包,可以提升打包速度
    2. 强打包缓存
      无需配置,webpack默认强缓存,如需配置,参见:缓存配置

新语法

  1. 开发常用方法
    1. ref 文档
    2. reactive 文档
    3. toRefs 文档
    4. getCurrentInstance 文档
    5. 生命周期钩子方法
  2. setup
    1. 参见官方文档
    2. 参数说明:
      1. Props,组件的props值
      2. context 对象,暴露了以前在this上暴露的属性,attrs,slots,emit
      3. 参数不能使用...展开,如使用展开操作,会使得参数丢失响应性

遇到的问题

  1. key值不更新问题
    原因:key值相同的情况下,vue认为组件无需更新,无论数据是否变动,均不会重新渲染组件
    解决方案:在需要重排组件内容的时候,重排时使用时间戳+id+循环序号作为key
  2. 双向绑定丢失响应
    1. 在setup内定义的属性,直接使用等于号赋值,无响应式
    2. 在setup内定义属性,值类型的使用ref,赋值使用 xxx.value,即可使用响应式;高级内容,需要使用reactive方法包裹,return时,如果展开需要使用toRefs方法包起来。