webpack
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
模块化规范
1.单例设计模式
解决问题模块间相互冲突的问题「闭包」 ; 模块间相互引用的问题;
不足之处需要手动分析模块之间的依赖关系按顺序依次导入相关模块所有模块都是基于script一个个导入的这样页面HTTP请求变多使用闭包+命名空间
A
let AModule = (function () { let name = "A"; const sum = function sum(...params) { let len = params.length, total = 0; if (len === 0) return 0; if (len === 1) return params[0]; params.forEach(item => { total += item; }); return total; }; return { sum }; })();
B
let BModule = (function () { let name = "B"; // 在average获取平均数的方法中我们需要用到A模块中的求和方法 const average = function average(...params) { let len = params.length, total = 0; if (len === 0) return 0; total = AModule.sum(...params); return total / len; }; return { average }; })();
Main
/* 在MAIN中我们需要用到A/B模块中的方法 */ console.log(AModule.sum(10, 20, 30, 40)); console.log(BModule.average(10, 20, 30, 40));
入口Index
<script src="A.js"></script> <script src="B.js"></script> <script src="main.js"></script>
2.AMDrequire.js
RequireJS
优势在保证模块之间独立和可以相互访问的基础上HTML中无需再导入各个模块了「不存在顺序问题」也不需要自己去分析相互间的依赖关系
不足依赖模块的导入是“前置导入”只有把依赖模块动态导入完毕才会触发回调函数执行「阻碍代码执行速度」代码书写的顺序也不是很方便可能存在重复导入
Main
这里引入 lib目录下的A和B
/* 全局配置 */ require.config({ // 声明后期所有模块的导入都在lib目录下找 baseUrl: './lib' }); require(['B', 'A'], function (B, A) { console.log(A.sum(10, 20, 30, 40)); console.log(B.average(10, 20, 30, 40)); });
lib
A
/* define([依赖的模块],function(用形参接收依赖的模块){ 写本模块中的代码 return { 把本模块中需要供外部调用的方法导出 }; }) */ define(function () { let name = "A"; const sum = function sum(...params) { let len = params.length, total = 0; if (len === 0) return 0; if (len === 1) return params[0]; params.forEach(item => { total += item; }); return total; }; // 导出模块方法 return { sum }; });
B:
// 定义B模块但是需要用到A模块中的方法所以最开始设置一下依赖 // 当后期加载B模块的时候先把A模块导入「去lib下找A.js」确保A模块导入后再把后面的函数执行 define(['A'], function (A) { // A存储的就是A模块中导出的方法 -> {sum:function...} let name = "B"; const average = function average(...params) { let len = params.length, total = 0; if (len === 0) return 0; total = A.sum(...params); return total / len; }; // 模块导出 return { average }; });
入口Index
<!-- IMPORT JS 导入require.min.js(AMD思想)的时候在全局提供两个函数 + require 导入模块 + define 定义模块 --> <script src="require.min.js"></script> <script src="main.js"></script>
3.Common.js规范
唯一的问题浏览器端不支持CommonJS规范
淘宝“玉伯”仿照CommonJS规范研发了一款插件 sea.js 旨在把CommonJS规范搬到浏览器端运行「这种模块思想被称之为CMD」
- exports
- module.exports
- require
main
/* CommonJS模块规范 导出模块 module.exports = { ... } module.exports = ... 导入模块 const xxx = require(模块地址「可以省略.js后缀」) 实现了真正的按需导入啥时候用啥时候导入即可 CommonJS模块规范实现了模块导入的缓存机制 导入一个模块会把这个模块中的代码执行获取其导出的内容「并且缓存起来」 当后续在遇到这个模块的导入不再重新把模块中的代码执行而是直接获取之前缓存中存储的导出的内容 只支持在Node或者webpack环境下运行不支持浏览器环境 */ const A = require('./A'); console.log(A.sum(10, 20, 30, 40)); const average = require('./B'); console.log(average(10, 20, 30, 40));
A
let name = "A"; const sum = function sum(...params) { let len = params.length, total = 0; if (len === 0) return 0; if (len === 1) return params[0]; params.forEach(item => { total += item; }); return total; }; /* 模块的导出 */ module.exports = { sum };
B
let name = "B"; /* 模块的导入把模块导出的内容获取到赋值给A */ const A = require('./A'); const average = function average(...params) { let len = params.length, total = 0; if (len === 0) return 0; total = A.sum(...params); return total / len; }; /* 模块导出 */ module.exports = average;
入口index
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=\, initial-scale=1.0"> <title>Document</title> </head> <body> <script src="main.js"></script> </body> </html>
4.ES6Module模块规范
ES6 入门教程
可以直接运行在浏览器端不支持IE
- export
- export default
- import
- import from
A
let name = "A"; const sum = function sum(...params) { let len = params.length, total = 0; if (len === 0) return 0; if (len === 1) return params[0]; params.forEach(item => { total += item; }); return total; }; /* 模块导出 */ export default sum; //Module对象{ default:sum函数 } //===================== /* 模块导出export 或者 export default 无论基于何种方式模块导出的永远是一个“Module对象” 第一种方式export + 一个模块中可以使用多次分别导出多项内容 + 导出的每一项内容都是给“Module对象”设置相关的成员 第二种方式export default + 一个模块中只能用一次 + 它是给“Module对象”设置一个叫做default的成员成员值是导出的内容 */ /* // 正确语法导出一个变量/值/创建函数表达式/对象... // export default sum; // export default 10; // export default function fn() { }; // export default { // x: 10, // name // }; // 错误语法 // export default let age = 12; */ /* // 语法不能直接导出一个变量/值必须在声明的时候“同时导出” // export name; //错误的写法 export let age = 14; //声明一个变量并且导出「Module对象.age=14」 export function fn() { }; export const obj = {}; // 语法可以导出一个对象(或代码块)其中包含多个我们需要导出的内容 export { name, //正确语法前提name是存在的我们把name赋值给Module对象值就是name变量的值 // x:10 //错误语法不能直接在这用键值对方式赋值 }; */
B
/* 模块导入sum => Module对象.default */ import sum from './A.js'; let name = "B"; const average = function average(...params) { let len = params.length, total = 0; if (len === 0) return 0; total = sum(...params); return total / len; }; /* 模块导出 */ export default average; //Module对象{ default:average函数 }
main
import sum from "./A.js"; import average from "./B.js"; console.log(sum(10, 20, 30, 40)); console.log(average(10, 20, 30, 40)); /* 模块导入把模块导出的“Module对象”中的每一项内容拿到 => import 导入模块的地址相对地址、不能省略后缀名「后期在weblack中可以省略」 语法一 import xxx from './A.js' 不是把“Module对象”整体导入进来赋值给xxx而是只拿到了“Module对象.default”属性值「xxx=Module对象.default」「换句话说基于export default xxx导出的内容用这种方式直接导入」 语法二 import {x,y} from './A.js' 用解构赋值的方式获取导出的内容首先不是把“Module对象.default属性值”进行解构赋值而是直接给“Module对象”解构赋值「换句话来讲它是获取基于 export let xxx=xxx 这种方式导出的内容」 语法三 import * as A from './A.js' 把模块导出的“Module对象”中的所有内容都拿到最后赋值给A「A=Module对象」 */ /* import A from './A.js'; console.log(A); import obj from './xxx.js'; //obj->0x001 import { x, m } from './xxx.js'; //不是把default导出的对象先获取然后解构赋值 // x-> Module.x -> undefined // m-> Module.m -> 100 import * as A from './xxx.js' // A.default -> 0x001 // A.m -> 100 export default { x: 10, y: 20 }; //对象0x001 => Module{ default:0x001 } export let m = 100; //=> Module{ default:0x001,m:100 } */
入口index
<!-- IMPORT JS + type="module" 是让JS支持ES6Module模块规范 + 预览要基于标准的HTTP协议LiveServer不能使用File文件协议 --> <script type="module" src="main.js"></script>
2.Webpack的基础操作
安装
// 为防止全局安装webpack导致版本冲突真实项目中以本地安装为主 $ npm init -y $ npm install webpack webpack-cli --save-dev OR $ yarn add webpack webpack-cli -D
零配置使用
/* * 默认会打包SRC目录中的JS文件入口默认index.js * 打包完成的目录默认是DIST/MAIN.JS * webpack默认支持CommonJS和ES6 Module的模块规范依此进行依赖打包 */ $ npx webpack
自定义基础配置
webpack.config.js
// Node内置的路径处理模块 const path = require('path'); // 导出自定义配置项 module.exports = { // 环境模式「生产环境production 开发环境development」 mode: 'production', // 打包入口「相对路径」 entry: './src/index.js', // 打包出口 output: { // 生成的文件名. [hash]创建随机哈希值 filename: 'bundle.[hash].js', // 打包地址「绝对路径」 path: path.resolve(__dirname, 'dist') } };
html-webpack-plugin
https://www.webpackjs.com/plugins/html-webpack-plugin/ $ yarn add html-webpack-plugin -D
const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { ... plugins: [ new HtmlWebpackPlugin({ // 指定视图模板 template: './public/index.html', // 编译后视图的名字 filename: 'index.html', // 是否压缩 minify: true }) ] }
多入口 & 多出口
const HtmlWebpackPlugin = require('html-webpack-plugin'); // HtmlWebpackPlugin需要配置多套 const htmlPlugins = ['index', 'login'].map(chunk => { return new HtmlWebpackPlugin({ template: `./public/${chunk}.html`, filename: `${chunk}.html`, // 指定导入的JS chunks: [chunk], minify: true }); }); module.exports = { mode: 'production', // 配置多入口 entry: { index: "./src/index.js", login: "./src/login.js", }, // 出口采用相同的规则 output: { filename: "[name].[hash].js", path: path.resolve(__dirname, "dist") }, plugins: [ ...htmlPlugins ] };
clean-webpack-plugin
yarn add clean-webpack-plugin -
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = { ... plugins: [ new CleanWebpackPlugin() ] };
webpack-dev-server
https://webpack.js.org/configuration/dev-server/ $ yarn add webpack-dev-server -D
/* paclage.json */ "scripts": { "start": "webpack server", "build": "webpack" } /* webpack.config.js */ module.exports = { ... devServer: { // 域名 host: '127.0.0.1', // 断口号 port: 3000, // GZIP压缩 compress: true, // 自动打开浏览器 open: true, // 热更新 hot: true, // 跨域代理 proxy: { "/jian": { target: "https://www.jianshu.com/asimov", changeOrigin: true, ws: true, pathRewrite: { "^/jian": "" } }, "/zhi": { target: "https://news-at.zhihu.com/api/4", changeOrigin: true, ws: true, pathRewrite: { "^/zhi": "" } } } } }; /* 测试接口 简书 https://www.jianshu.com/asimov/subscriptions/recommended_collections 知乎 https://news-at.zhihu.com/api/4/news/latest */
处理样式的loader「加载器」
$ yarn add css-loader style-loader less less-loader autoprefixer postcss-loader -D
module.exports = { ... /* 配置模块加载器LOADER执行顺序从右向左、从下向上 */ module: { rules: [{ test: /\.(css|less)$/, // 基于正则匹配哪些模块需要处理 use: [ "style-loader", // 把CSS插入到HEAD中 "css-loader", // 编译解析@import/URL()这种语法 "postcss-loader", // 设置前缀 "less-loader" // 把LESS编译为CSS ] }] } };
postcss.config.js
module.exports = { plugins: [ require('autoprefixer') ] }; // 或者在配置项中直接处理 { loader: "postcss-loader", options: { postcssOptions: { plugins: [ require('autoprefixer') ] } } },
package.json OR .browserslistrc
// https://github.com/browserslist/browserslist "browserslist": [ "> 1%", "last 2 versions", "not dead" ]
mini-css-extract-plugin 抽离CSS样式 /ˈekstrækt/
https://www.npmjs.com/package/mini-css-extract-plugin $ yarn add mini-css-extract-plugin -D
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { ... plugins: [ ... new MiniCssExtractPlugin({ // 设置编译后的文件名字 filename: 'main.[hash].css' }) ], ... module: { rules: [{ test: /\.(css|less)$/, use: [ // 使用插件中的LOADER代替STYLE方式 MiniCssExtractPlugin.loader, ... ] }] } };
基于babel实现ES6的转换
$ yarn add babel babel-loader @babel/preset-env @babel/core -D $ yarn add @babel/polyfill
module.exports = { ... module: { rules: [{ test: /\.js$/, use: ['babel-loader'], // 设置编译时忽略的文件和指定编译目录 include: path.resolve(__dirname, 'src'), exclude: /node_modules/ }] } };
babel.config.js
module.exports = { presets: [ "@babel/preset-env" ] };
index.js
import '@babel/polyfill';
设置优化项压缩CSS/JS
$ yarn add css-minimizer-webpack-plugin terser-webpack-plugin -D
const TerserPlugin = require('terser-webpack-plugin'); const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin'); module.exports = { optimization: { // 设置压缩方式 minimizer: [ new CssMinimizerWebpackPlugin(), new TerserPlugin() ] } };
设置解析器配置别名
module.exports = { resolve: { alias: { '@': path.resolve(__dirname, './src') } } };
图片的处理
$ yarn add file-loader url-loader -D
module.exports = { ... module: { rules: [ ... { test: /\.(png|jpe?g|gif)$/i, type: 'javascript/auto', use: [{ // 把指定大小内的图片BASE64 loader: 'url-loader', options: { limit: 200 * 1024, esModule: false, name: 'images/[name].[hash].[ext]' } }] }] }, // 设置打包的最大资源大小 performance: { maxAssetSize: 100 * 1024 * 1024 * 1024, maxEntrypointSize: 100 * 1024 * 1024 * 1024 } };