webpack工程化实战

Posted 前端小嘟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了webpack工程化实战相关的知识,希望对你有一定的参考价值。

随着前端技术发展,掌握前端工程化越发成为前端工程师必要的条件。日常开发者中,我们常在用例如vue-cli 、 create-react-app等脚手架构建我们项目。但是如果你想在团队脱颖而出,那就需要花费一部分时间去学习webpack



本文各插件版本将在文章末尾为大家呈现

 


初始化项目

新建目录,初始化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.jsconst path = require('path')module.export = {  entry:path.resolve(__dirname,'./src/index.js'//入口文件  output:{    filename:'bundle.js'//打包后的文件名称    path:path.resolve(__dirname,'./build'//打包后的目录  }}

配置完webpack.config.js之后可以尝试着新建文件打包一下



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-loader 的作用是将 CSS 模块转换为一个 JS 模块,但不会使用这个模块  style-loader 就可以把css-loader转化的模块通过style标签追加到页面上 //是将 css 装载到 javascript;style-loader 是让 javascript 认识css,require 时用使用less构建样式npm i -D less less-loader //less-loader 是将less转化为css

配置文件如下

// webpack.config.jsmodule.exports = { // ...省略其他配置 module:{ rules:[ { test:/\.css$/, use:['style-loader','css-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-decoratorsnpm i -D @babel/plugin-transform-arrow-functions @babel/plugin-transform-runtime



1.8 打包 图片 字体等

在webpack5中  svg属于inline asset,内置loader

图片字体等文件在webpack5中内置了loader,按照如下配置即可无需引入依赖,路径自己定义即可。

//以上配置省略module:{ rules:[ { test: /\.vue$/, loader: 'vue-loader' }, { // 用正则去匹配要用该 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]' } } ] },



2搭建vue开发环境

   结合上面例子的依赖,还需要以下几种配置


 


2.1解析vue文件

    

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

 vue-loader 用于解析.vue文件

vue-template-compiler 用于编译模板


具体配置见后文完整配置


 


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


devServer: { contentBase: path.join(__dirname, './dist'), compress: true, port: 3000, hot: true, open: true },



2.3 配置打包命令 

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


在src下新建index.js

import Vue from 'vue'import App from './App.vue'new Vue({ render: h => h(App)}).$mount('#app')
if (module.hot) { module.hot.accept('./App.vue', function () { }) module.hot.accept('./show.js', function () { })}



新建App.vue

<template> <div id="app">{{str}}</div></template><script>console.log('app 加载了')export default { name: 'App', data () { return { str: 'hello3' } }, mounted () { console.log(Math.trunc(12.1)) }, methods: {
}}</script>



新建 index.html

<!DOCTYPE html><html lang="en"><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> <!-- <p id="app"></p> <img id="testimg" alt=""> <div>bfg</div> --> <div id="app"></div></body></html>

新建完各页面  执行npm run dev如果浏览器出现Vue开发环境成功,那么一个基础的基于vue的webpack打包工具就完成了



完整配置

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, './dist'), compress: true, port: 3000, hot: true, open: true }, devtool: 'source-map', module:{ rules:[ { test: /\.vue$/, loader: 'vue-loader' }, { // 用正则去匹配要用该 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.ejc', template:path.resolve(__dirname,'./src/index.html') }), new CleanWebpackPlugin(), new webpack.HotModuleReplacementPlugin(), new VueLoaderPlugin() ]}


 


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

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


新建两个文件夹

  • 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




开发环境配置

// 开发环境// webpack中引入的path[require('path')]是node.js内置的package,用来处理路径的。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') //压缩cssconst UglifyJsPlugin = require('uglifyjs-webpack-plugin') //压缩jsmodule.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", "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" }}


交流讨论

到目前为止,我们已经成功的自己搭建了一个基础vue 开发环境,不过想要获取更好的offer、更高的薪水,还需要继续深入学习,在此来一波预告吧《webpack工程化实战(2)优化webpack配置》,看完文章,同学们可以搭建一下,搭建过程中会遇到许多的坑,同学可以在后台留言,或者微信私信我都可以,好了这一期就到这里了。

如果觉得这篇文章还不错,来个【分享、点赞、在看】三连吧,让更多的人也看到~

以上是关于webpack工程化实战的主要内容,如果未能解决你的问题,请参考以下文章

工程化实战 - 用 webpack 搭建一个大型项目

Webpack 工程的 PWA 实战

Webpack + React全栈工程架构项目实战精讲

Webpack + React全栈工程架构项目实战精讲

Webpack + React全栈工程架构项目实战精讲

来和 webpack 谈场恋爱吧