前言 webpack 当下最流行的前端架构工具。
Webpack 是一个打包模块化 JavaScript 的工具,在 Webpack 里一切文件皆模块,通过 Loader 转换文件,通过 Plugin 注入钩子,最后输出由多个模块组合成的文件。Webpack 专注于构建模块化项目。
一切文件:JavaScript、CSS、SCSS、图片、模板,在 Webpack 眼中都是一个个模块,这样的好处是能清晰的描述出各个模块之间的依赖关系,以方便 Webpack 对模块进行组合和打包。 经过 Webpack 的处理,最终会输出浏览器能使用的静态资源。
webpack
为什么需要模块化 模块化是指把一个复杂的系统分解到多个模块以方便编码。 在没有出现模块化概念时,解决 js 代码组织问题,比较流行的是命名空间的方式,如 JQuery。 但无法解决以下问题:
命名空间冲突,两个库可能会使用同一个名称,例如 Zepto 也被放在 window.\$ 下;
无法合理地管理项目的依赖和版本;
无法方便地控制依赖的加载顺序。
当项目特别庞大并依赖特别多时,加载的类库就会显得特别笨重,在显示上会出现页面不流畅的情况,在代码上,会出现难以维护的现象。
而技术的发展就是为了编写更好易懂的代码,减少人为规范,而出现模块化开发,象征的就是 requirejs、CommonJS、seaJs 等等,而后随着 js 发展,ECMA 提出的 JavaScript 模块化规范 ES6 模块化
,它将逐渐取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
ES6 模块化例子:
1 2 3 4 5 6 7 // 导出 export const method = ()=>"ES6 模块化"; export default { es6:"es6 代码" } // 导入 import React, { useState } from "react";
ES6 模块化虽然是模块化解决方案,但浏览器支持力不高,所以需要架构工具帮助转义为浏览器可识别的代码。
工具对比 scripts npm 是在安装 Node.js 时附带的包管理器,Npm Script 则是 Npm 内置的一个功能,允许在 package.json 文件里面使用 scripts 字段定义任务。
1 2 3 4 5 6 { "scripts":{ "dev": "webpack-dev-server", "start":"node script/index" } }
里面的 scripts 字段是一个对象,每个属性对应一段 Shell 脚本,以上代码定义了两个任务 dev 和 pub。 其底层实现原理是通过调用 Shell 去运行脚本命令,如执行npm run start
,执行的是node script/index
node 程序,但这个功能很单一,没法使用与复杂场景。
Gulp Gulp 是一个基于流的自动化构建工具。 除了可以管理和执行任务,还支持监听文件、读写文件。Gulp 被设计得非常简单,只通过下面 5 个方法就可以胜任几乎所有构建场景:
1 2 3 4 5 通过 gulp.task 注册一个任务; 通过 gulp.run 执行任务; 通过 gulp.watch 监听文件变化; 通过 gulp.src 读取文件; 通过 gulp.dest 写文件。
它的特点流的方式,而且使用也比较简单,把需要编译内容分成一个个任务gulp.task
编写即可,而在任务中使用gulp.src
读取文件,而它可以正则,使用方式如 glob
,然后使用pipe
传递加载到文件内容给插件,而 gulp 的插件编写也比较简单,基本上都是对 node 的使用,最后经过 pipe 传递给gulp.dest
进行输出操作,这些是 gulp 基本操作,以下为一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 // 引入 Gulp var gulp = require('gulp'); // 引入插件 var jshint = require('gulp-jshint'); var sass = require('gulp-sass'); var concat = require('gulp-concat'); var uglify = require('gulp-uglify'); // 编译 SCSS 任务 gulp.task('sass', function() { // 读取文件通过管道传给插件 gulp.src('./scss/*.scss') // SCSS 插件把 scss 文件编译成 CSS 文件 .pipe(sass()) // 输出文件 .pipe(gulp.dest('./css')); }); // 合并压缩 JS gulp.task('scripts', function() { gulp.src('./js/*.js') .pipe(concat('all.js')) .pipe(uglify()) .pipe(gulp.dest('./dist')); }); // 监听文件变化 gulp.task('watch', function(){ // 当 scss 文件被编辑时执行 SCSS 任务 gulp.watch('./scss/*.scss', ['sass']); gulp.watch('./js/*.js', ['scripts']); });
Gulp 的优点是好用又不失灵活,既可以单独完成构建也可以和其它工具搭配使用。其缺点是集成度不高,要写很多配置后才可以用,无法做到开箱即用。 还一点就是不会像 webpack 那样把所有文件看作模块打包成一个整体,它是把当前处理的文件看成一个整体,所以处理多少个文件就输入多少个文件。
Rollup Rollup 是一个和 Webpack 很类似但专注于 ES6 的模块打包工具。 Rollup 的亮点在于能针对 ES6 源码进行 Tree Shaking 以去除那些已被定义但没被使用的代码,以及 Scope Hoisting 以减小输出文件大小提升运行性能。 然而 Rollup 的这些亮点随后就被 Webpack 模仿和实现。 由于 Rollup 的使用和 Webpack 差不多,而它们的差别是:
1 2 3 4 Rollup 是在 Webpack 流行后出现的替代品; Rollup 生态链还不完善,体验不如 Webpack; Rollup 功能不如 Webpack 完善,但其配置和使用更加简单; Rollup 不支持 Code Spliting,但好处是打包出来的代码中没有 Webpack 那段模块的加载、执行和缓存的代码。
Rollup 在用于打包 JavaScript 库时比 Webpack 更加有优势,因为其打包出来的代码更小更快。 但功能不够完善,很多场景都找不到现成的解决方案。
webpack 前言以对它进行简述了。 Webpack 的优点是:
1 2 3 4 5 专注于处理模块化的项目,能做到开箱即用一步到位; 通过 Plugin 扩展,完整好用又不失灵活; 使用场景不仅限于 Web 开发; 社区庞大活跃,经常引入紧跟时代发展的新特性,能为大多数场景找到已有的开源扩展; 良好的开发体验。
Webpack 的缺点是只能用于采用模块化开发的项目。
webpack 的简单使用 安装 webpack 安装
1 npm i webpack webpack-cli -D
babel 安装
1 2 3 4 5 6 7 npm i -D @babel/cli @babel/core babel-loader // babel 插件 npm i -D @babel/plugin-transform-arrow-functions @babel/plugin-transform-arrow-functions // babel presets npm i -D @babel/preset-env @babel/preset-react
css 处理安装
1 npm i -D css-loader postcss-loader postcss-normalize postcss-preset-env postcss-flexbugs-fixes mini-css-extract-plugin style-loader
当打包时使用 mini-css-extract-plugin 分离 css 为单独文件,而开发时使用 style-loader 在项目热加载模式下使用,因为 mini-css-extract-plugin 在热加载使用不了。
html 处理
1 npm i -D html-webpack-plugin
完整 package.json 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 { "name": "webpack01", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "w": "webpack" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@babel/cli": "^7.8.0", "@babel/core": "^7.8.0", "@babel/plugin-proposal-class-properties": "^7.8.0", "@babel/plugin-transform-arrow-functions": "^7.8.0", "@babel/preset-env": "^7.8.0", "@babel/preset-react": "^7.8.0", "babel-loader": "^8.0.6", "css-loader": "^3.4.2", "html-webpack-plugin": "^3.2.0", "mini-css-extract-plugin": "^0.9.0", "postcss-flexbugs-fixes": "^4.1.0", "postcss-loader": "^3.0.0", "postcss-normalize": "^8.0.1", "postcss-preset-env": "^6.7.0", "webpack": "^4.41.5", "webpack-cli": "^3.3.10" }, "dependencies": { "@babel/polyfill": "^7.8.0", "react": "^16.12.0", "react-dom": "^16.12.0" } }
完整 webpack.config.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const postcssNormalize = require("postcss-normalize"); const babel = require("./babel.config"); const moduleFileExtensions = [ "web.mjs", "mjs", "web.js", "js", "json", "web.jsx", "jsx" ]; module.exports = { entry: "./src/index.jsx", output: { // path: '/dist', path: __dirname + "/dist", filename: "bundle.js" }, resolve: { extensions: moduleFileExtensions.map(ext => `.${ext}`) }, module: { rules: [ { test: /\.jsx$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { ...babel } } }, { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, "css-loader", { loader: require.resolve("postcss-loader"), options: { ident: "postcss", plugins: () => [ require("postcss-flexbugs-fixes"), require("postcss-preset-env")({ autoprefixer: { flexbox: "no-2009" }, stage: 3 }), postcssNormalize() ], sourceMap: true } } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: "[name].[chunkhash:8].css", chunkFilename: "[id].css" }), new HtmlWebpackPlugin({ inject: true, template: "public/index.html" }) ] };
babel.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const presets = [ "@babel/preset-react", [ "@babel/preset-env", { useBuiltIns: "entry" } ] ]; const plugins = [ "@babel/plugin-transform-arrow-functions", "@babel/plugin-proposal-class-properties" ]; module.exports = { presets, plugins };