webpack5.0工程化实战(1)搭建vue开发环境

2,159 阅读3分钟

前言

前段时间由于工作需要及个人兴趣,对webpack进行了一些学习,查阅很多文档发现,各插件版本更新迭代较快,安装中会产生一些报错,所以想写一个最新版的webpack供大家学习

简介

webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部构建一个 依赖图(dependency graph),此依赖图对应映射到项目所需的每个模块,并生成一个或多个 bundle

webpack文档

​​

正文

1.初始化项目

新建目录,初始化npm

npm init

1.1安装webpack包(建议将三个包同时安装)

npm i -D webpack webpack-cli webpack-dev-server
  • npm i -D 为npm install --save-dev的缩写
  • npm i -S 为npm install --save的缩写

1.2配置webpack.config.js

新建webpack.config.js,实现更多的自定义配置

webpack.config.js

const path = require('path')

module.export  ={
    entry:path.reslove(`__dirname,'./src/index.js'`) //入口文件
    output:{
    // 把所有依赖的模块合并输出到一个 bundle.js 文件
        filename:'bundle.js', //打包后的文件名称
        path:path.resolve(__dirname,'./build') //打包后的目录
    }

}

1.3安装各插件

html-webpack-plugin
使用html-webpack-plugin创建html页面,并将打包后的js文件需要引入到html中  使用该插件引入对应的js文件

npm i -D html-webpack-plugin 
//新建build同级的文件夹 public 新建一个index.html
clean-webpack-plugin 

npm i -D clean-webpack-plugin
将dist文件夹残留上次打包的文件清空

1.4 引入css

在引入css时也需要一些loader来解析我们的css

npm i -D style-loader css-loader

css的作用是将css模块转化为JS模块,但不会使用这个模块

style-loader 就可以把css-loader转化的模块通过style标签追加到页面上

使用less构建样式
npm i -D less less-loader

配置文件如下

// webpack.config.js

module.exports ={
//...省略其他配置
module:{
      rules:[{
           // 用正则去匹配要用该 loader 转换的 CSS 文件
                test: /\.css$/,
                exclude: path.resolve(__dirname, 'node_modules'),
                use: [ 'style-loader', 'css-loader','postcss-loader'] // 切记从右向左解析原则
            },
            {
                test:/.less$/, 
                use:['style-loader','css-loader','less-loader'] // 从右向左解析原则   
            } 
      ]
    }

}

1.5 为css添加浏览器前缀

npm i -D postcss-loader autoprefixer  //为了兼容各浏览器  添加此插件

需要在根目录创建postcss.config.js 文件 

module.exports = {    plugins: [require('autoprefixer')]  // 引用该插件}

1.6 把css样式从js文件中提取到单独的css文件中

npm i -D mini-css-extract-plugin

const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
        //...省略其他配置
        module: {   
        rules: [  
        {   test: /.less$/,   
        use: [   MiniCssExtractPlugin.loader,   
        'css-loader',   
        'less-loader'   ],
            }   
          ]   
        },   
        plugins: [
        new MiniCssExtractPlugin({
        filename: "[name].[hash].css",
        chunkFilename: "[id].css", 
                   }) 
            ] 
        }
        //需要配置 MiniCssExtractPlugin 插件 一起使用


1.7   babel转义js文件

需要注意版本对应的关系

ES6/7/8 转 ES5代码

安装插件

npm i -D babel-loader @babel/preset-env @babel/core @babel/plugin-proposal-decorators

npm i -D @babel/plugin-transform-arrow-functions @babel/plugin-transform-runtime
rules:[
            {
                test: /\.m?js$/,
                exclude: /node_modules/,
                use: {
                  loader: 'babel-loader',
                  options: {
                    // 缓存,加快babel-loader编译速度
                    cacheDirectory: true,
                    // 一系列插件的集合,包括处理箭头函数等,配置后是否需要配置plugins? 后面再看
                    // 2021/5/12 结论:不需要配置其他plugins
                    // useBuiltIns corejs 解决es6新增api无法编译问题(只能编译语法,例如箭头函数)
                    presets: [
                      // ['@babel/preset-env', { targets: 'defaults' }]
                      ['@babel/preset-env', { useBuiltIns: 'usage', corejs: 3, targets: 'defaults' }]
                    ],
                    plugins: [
                      // 编译箭头函数
                      '@babel/plugin-transform-arrow-functions',
                      // 编译装饰器
                      ['@babel/plugin-proposal-decorators', { legacy: true }],
                      // 编译类,loose true时是赋值法定义属性,false时是使用Object.defineProperty定义属性,后者是默认
                      ['@babel/plugin-proposal-class-properties', { loose: false }]
                    ]
                  }
                }
              },]

1.8 打包 图片 字体等

webpack5中  svg属于inline asset,内置loader

           [
            {
                test:/\.svg$/,
                type:'asset/inline',
                generator:{
                  filename:"icons/[name]--[hash].[ext]"
                }
              },
              {
                test:/\.(png|jpe?g|gif)(\?.*)?$/,
                type:'asset/resource',
                generator: {
                  filename: 'imgs/[name]--[hash].[ext]'
                }
              },
              {
                test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
                type: 'asset/resource',
                generator: {
                  filename: 'media/[name]--[hash].[ext]'
                }
              },
              {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
                type: 'asset/resource',
                generator: {
                  filename: 'fonts/[name]--[hash].[ext]'
                }
              }
              ]

2.搭建vue开发环境

接下来搭建一个vue开发环境 结合上面例子的依赖,还需要以下几种配置

2.1解析vue文件

npm安装插件

npm i -D vue-loader vue-template-compiler vue-style-loader

npm i -S vue

vue-loader 用于解析.vue文件

vue-template-compiler 用于编译模板

具体配置见后文完整配置

2.2 配置webpack-dev-server进行热更新

devServer:{
      port:3000,
      hot:true,
      open:truecontentBase:'./build'
    },

2.3 配置打包命令

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --watch --config webpack.config.js",
    "dev": "webpack server "
  },

2.3.1 在src下新建index.js

import Vue from 'vue'
import App from './App.vue'
new Vue({
  render: h => h(App)
}).$mount('#app')

2.3.2 新建App.vue

<template>
  <div class="style" id="app">
    {{ str }}

    <canvas id="myCanvas" width="600" height="400" ref="can"></canvas>
  </div>
</template>

<script>
console.log("加载");
export default {
    name:'App',
    data (){
    return{
        str:'hello'
          }
    },
    mounted (){
    console.log(Math.trunc(12.1))
    },
    method:{}
}
</script>

2.3.3 新建index.html

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id='app'>
       
    </div>
</body>

</html>

2.3.4 完整配置

2.4 区分开发环境与生产环境

实际运用到项目时,  需要区分开发环境与生产环境的关系

const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin') //引入对应文件
const webpack = require('webpack')
const { VueLoaderPlugin } = require('vue-loader')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
//将css样式从js文件中提取到单独css文件
// const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// 默认路径,将entry的前置路径放到这个字段
    // context:path.resolve(__dirname,'src'),
    entry:path.resolve(__dirname,'./src/index.js'),
    output:{
        // 把所有依赖的模块合并输出到一个 bundle.js 文件
        filename:'[name].[hash:8].js',
        path:path.resolve(__dirname,'./build')
    },
    devServer: {
        contentBase: path.join(__dirname, './build'),
        compress: true,
        port: 4000,
        hot: true,
        open: true
      },
      devtool: 'source-map',
    module:{
        rules:[
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            // css-loader 是将 css 装载到 javascript;style-loader 是让 javascript 认识css将css挂载到style,require 时用
            {
                // 用正则去匹配要用该 loader 转换的 CSS 文件
                test: /\.css$/,
                exclude: path.resolve(__dirname, 'node_modules'),
                use: [ 'style-loader', 'css-loader','postcss-loader']
            },
            {
                test: /\.m?js$/,
                exclude: /node_modules/,
                use: {
                  loader: 'babel-loader',
                  options: {
                    // 缓存,加快babel-loader编译速度
                    cacheDirectory: true,
                    // 一系列插件的集合,包括处理箭头函数等,配置后是否需要配置plugins? 后面再看
                    // 2021/5/12 结论:不需要配置其他plugins
                    // useBuiltIns corejs 解决es6新增api无法编译问题(只能编译语法,例如箭头函数)
                    presets: [
                      // ['@babel/preset-env', { targets: 'defaults' }]
                      ['@babel/preset-env', { useBuiltIns: 'usage', corejs: 3, targets: 'defaults' }]
                    ],
                    plugins: [
                      // 编译箭头函数
                      '@babel/plugin-transform-arrow-functions',
                      // 编译装饰器
                      ['@babel/plugin-proposal-decorators', { legacy: true }],
                      // 编译类,loose true时是赋值法定义属性,false时是使用Object.defineProperty定义属性,后者是默认
                      ['@babel/plugin-proposal-class-properties', { loose: false }]
                    ]
                  }
                }
              },
              {
                test:/\.svg$/,
                type:'asset/inline',
                generator:{
                  filename:"icons/[name]--[hash].[ext]"
                }
              },
              {
                test:/\.(png|jpe?g|gif)(\?.*)?$/,
                type:'asset/resource',
                generator: {
                  filename: 'imgs/[name]--[hash].[ext]'
                }
              },
              {
                test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
                type: 'asset/resource',
                generator: {
                  filename: 'media/[name]--[hash].[ext]'
                }
              },
              {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
                type: 'asset/resource',
                generator: {
                  filename: 'fonts/[name]--[hash].[ext]'
                }
              }
        ]
    },
    plugins:[
        new htmlWebpackPlugin({
            filename:'index.html',
            template:path.resolve(__dirname,'./src/index.ejs'),
            minify: {
              collapseWhitespace: true,
              removeAttributeQuotes: true,
              removeComments: true,
              minifyJS: true,
              minifyCSS: true
            },
        }),
        new CleanWebpackPlugin(),
        new webpack.HotModuleReplacementPlugin(),
        new VueLoaderPlugin()
    ]
}

新建两个文件夹

  • webpack.dev.js 开发环境使用
  • webpack.prod.js 生产环境使用
  • webpack.config.js 公用配置

2.4.1 开发环境

  1. 不需要压缩代码
  2. 需要热更新
  3. css不需要提取到css文件
  4. devtool使用source-map
  5. ...

2.4.2 生产环境

  1. 压缩代码
  2. 不需要热更新
  3. 提取css,压缩css文件
  4. sourceMap
  5. 构建前清除上一次构建的内容
  6. ...

2.4.3 安装所需依赖

npm i optimize-css-assets-webpack-plugin mini-css-extract-plugin clean-webpack-plugin webpack-merge copy-webpack-plugin -D
  • webpack-merge 合并配置
  • copy-webpack-plugin 拷贝静态资源
  • optimize-css-assets-webpack-plugin 压缩css
  • uglifyjs-webpack-plugin 压缩js

开发环境配置

//开发环境
const webpackConfig = require('./webpack.config.js')
const WebpackMerge = require('webpack-merge')
const webpack = require('webpack')

// Webpack-merge 提供了一个函数,该函数将数组串联并合并创建新对象的对象。如果遇到函数,它将执行它们,通过算法运行结果,然后再次将返回的值包装在函数中。

module.exports = WebpackMerge.merge(webpackConfig,{
mode:'development', 
devtool:'source-map',
devServer:{ 
port:3000, 
hot:true, 
contentBase:'./build' 
}, 
plugins:[ 
    new webpack.HotModuleReplacementPlugin()] 
}) 

生产环境配置

const path = require('path')
const webpackConfig = require('./webpack.config.js')
const WebpackMerge = require('webpack-merge')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') //压缩css
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')       //压缩js
module.exports = WebpackMerge.merge(webpackConfig,{
    mode:'production',
    devtool:'cheap-module-source-map',
    plugins:[
      new CopyWebpackPlugin({
        patterns:[{
        from:path.resolve(__dirname,'src'),
        to:path.resolve(__dirname,'build')
      }]}),
    ],
    optimization:{
      minimizer:[
        new UglifyJsPlugin({//压缩js
          cache:true,
          parallel:true,
          sourceMap:true
      }),
      new OptimizeCssAssetsPlugin({})
      ],
      splitChunks:{
        chunks:'all',
        cacheGroups:{
          libs: {
            name: "chunk-libs",
            test: /[\\/]node_modules[\\/]/,
            priority: 10,
            chunks: "initial" // 只打包初始时依赖的第三方
          }
        }
      }
    }
  })
  

修改package.json 配置

{
  "name": "webpack-new",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --watch --config webpack.prod.js",
    "dev": "webpack-dev-server --config webpack.dev.js"
  },
  "author": "",
  "license": "ISC",
  "keywords": [],
  "description": "",
  "devDependencies": {
    "@babel/core": "^7.14.6",
    "@babel/plugin-proposal-decorators": "^7.14.5",
    "@babel/plugin-transform-arrow-functions": "^7.14.5",
    "@babel/plugin-transform-runtime": "^7.14.5",
    "@babel/preset-env": "^7.14.7",
    "@intervolga/optimize-cssnano-plugin": "^1.0.6",
    "autoprefixer": "^10.2.6",
    "babel-loader": "^8.2.2",
    "clean-webpack-plugin": "^3.0.0",
    "copy-webpack-plugin": "^9.0.1",
    "css-loader": "^5.2.6",
    "html-webpack-plugin": "^5.3.2",
    "mini-css-extract-plugin": "^1.6.2",
    "optimize-css-assets-webpack-plugin": "^6.0.1",
    "postcss-loader": "^6.1.0",
    "style-loader": "^2.0.0",
    "uglifyjs-webpack-plugin": "^2.2.0",
    "vue": "^2.6.14",
    "vue-loader": "^15.9.7",
    "vue-router": "^3.5.2",
    "vue-template-compiler": "^2.6.14",
    "vuex": "^3.6.2",
    "webpack": "^5.40.0",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.2",
    "webpack-merge": "^5.8.0"
  }
}

gitee地址

交流讨论

看完文章,同学们可以搭建一下,搭建过程中会遇到许多的坑,同学可以在下方留言。想要获取更多前端资料,关注公众号:前端小嘟 后台留言拉你进前后端交流群