webpack4

https://webpack.docschina.org/

一、Webpack快速上手
yarn init --yes   初始化package.json

yarn add webpack webpack-cli --dev  安装webpack 和cli模块

yarn webpack --version   查看版本

yarn webpack  打包
二、ebpack 配置文件

会按照约定打包
src/index ----> dist/main.js
在根目录下添加文件webpack.config.js

const path = require('path')
module.exports = {
    entry:'./src/main.js',     //指定webpack打包入口文件路径
     output:{
        filename:'dist111.js',    //打包输出文件的名称
        path:path.join(__dirname,'output'),   //打包输出文件路径
    }
}
三、Webpack 工作模式
// webpack.development.config.js
module.exports = {
  mode: 'development',
};

// webpack.production.config.js
module.exports = {
  mode: 'production',
};

// webpack.custom.config.js
module.exports = {
  mode: 'none',
};

如果要根据 webpack.config.js 中的 mode 变量更改打包行为,则必须将配置导出为函数,而不是导出对象:

var config = {
  entry: './app.js',
  //...
};

module.exports = (env, argv) => {
  if (argv.mode === 'development') {
    config.devtool = 'source-map';
  }

  if (argv.mode === 'production') {
    //...
  }

  return config;
};
四、Webpack 资源模块加载

Webpack默认只会解析js代码


image.png
安装css-loader
yarn add css-loader --dev

webpack.config.js

module.exports={
  module:{
    rules:[
      {
          test:'/.css$/',
          use:'style-loader',//先执行css-loader,倒序执行
          use:'css-loader'
      }
    ]
  }
}
安装style-loader
yarn add style-loader --dev  
作用:将css-loader转换的结果通过style标签的形式追加到页面上

Loader是Webpack的核心特性,借助于Loader就可以加载任何类型的资源

五、webpack 导入资源模块

JavaScript驱动整个前端应用

import './heading.css'
六、webpack 文件资源加载器Loader
yarn add file-loader --dev

webpack.config.js

output:{
  publicPath:'dist/'  
},
module:{
  rules:[
    {
      test:/.css$/,
      use:[
        'style-loader',
        'css-loader',
      ]
    },{
      test:/.png$/,
      use:'file-loader'
    }
  ]
}
image.png
七、webpack URL 加载器 url-Loader
安装url-loader
yarn add url-loader --dev
module:{
  rules:[
    {
      test:/.css$/,
      use:[
        'style-loader',
        'css-loader',
      ]
    },{
      test:/.png$/,
      use:'url-loader'
    }
  ]
}
yarn webpack   打包测试

小文件使用Data URLs,减少请求次数
大文件单独提取存放,提高加载速度

module:{
  rules:[
    {
      test:/.css$/,
      use:[
        'style-loader',
        'css-loader',
      ]
    },{
      test:/.png$/,
      use:{
        loader:'url-loader',
        options:{
            limit:10 * 1024,    //限制10K一下的用url-loader 转换
        }
      }
    }
  ]
}

超出10KB文件单独提取存放,小于10KB文件转换为Data URLs 嵌入代码,需要同时安装file-loader,url-loader对于超出的文件会去调用file-loader模块

八、Webpack 常用加载器分类
  • 编译转换类型 :加载到的资源模块转换为js代码,例如css-loader
  • 文件操作类型:加载到的资源模块拷贝到输出目录,同时将文件访问路径向外导出,例如file-loader
  • 代码检查类:对于加载到的资源文件进行校验的加载器,目的是为了统一代码风格 例如eslint-loader
九、Webpack 与ES2015

因为模块打包需要,所以处理import和export,并不能够转换代码中其他的es6特性,要转换es6,const等语法,需要安装babel-loader,而babel-loader 需要依赖babel的核心模块,所以需要同时安装@babel/core和用于转换具体特性转换插件的集合 @babel/preset-env

yarn add babel-loader @babel/core @babel/preset-env --dev

webpack.config.js

module:{
  rules:[
    {
      test:/.js$/,
      use:{
        loader:'babel-loader',
        options:{
            presets:['@babel/preset-env']
        }
      }
    }
  ]
}


再次运行yarn  webpack

Webpack 只是打包工具,加载器可以用力编译转换代码

十、Webpack加载资源的方式
  • 一、遵循ES Modules 标准的import声明
  • 二、遵循CommonJS标准的require函数
  • 三、遵循AMD标准的define函数和require函数
    还有一些独立的加载器在工作的时候也会去处理加载的资源当中导入的模块,例如css-loader 加载的文件,@import指令和url函数也会触发相应的资源模块的加载,html-loader 加载的html文件当中的图片标签的src属性也会触发相应的模块加载。
安装html-loader
yarn add html-loader --dev

webpack.config.js

module:{
  rules:[
    {
      test:/.html$/,
      use:{
        loader:'html-loader',
        options:{
            attrs:['img:src','a:href']
        }
      }
    }
  ]
}
  • 四、*样式代码中的@import指令和url函数
  • 五、 * HTML代码中图片标签的src属性
十一、Webpack 核心工作原理

image.png

根据配置找到入口文件作为打包的入口,一般为js文件,根据代码中的import或者require 来解析推断文件依赖的模块,分别去解析每一个资源模块对应的依赖,就会形成整个项目中用到的所有文件的依赖关系的依赖树 ,webpack会递归这个依赖树,找到每个节点对应的资源文件,最后根据配置文件中的rules属性找到这个模块对应的加载器,然后加载这个模块,最后会将加载到的结果放到打包结果文件当中,从而去实现整个项目的打包,Loader 机制是Webpack 的核心

十二、Webpack Loader的工作原理(开发一个Loader )
image.png

image.png

image.png
yarn add marked --dev

// markdown-loader.js
const marked = require('marked')

module.exports = source =>{
    // return 'console.log("hello")';//必须是js
    const html = marked(source)
    
    // return `module.exports = ${JSON.stringify(html)}`;//方法1
    // return `exports default = ${JSON.stringify(html)}`;//方法2

    // 返回html 字符串交给下一个loader处理
    return html

}

// webpack.config.js
module:{
    rules:[
        {
            test:/.md$/,
            use:[
                'html-loader',
                './markdown-loader.js'
            ]
        }
    ]
}

Loader 负责资源文件从输入到输出的转换,loader 是一个管道的概念,对于同一个资源可以依次使用多个Loader,例如css-loader---->style-loader等

十三、Webpack 插件机制介绍

插件目的是为了增强Webpack 自动化能力,Loader是实现资源模块的加载从而实现整体项目的打包,而Plugin解决其他的自动化工作,例如在打包之前plugin可以帮我们清除dist目录,可以用来拷贝静态文件至输出目录,压缩输出的代码。webpack + plugin 实现了大多数前端工程化工作。
webpack != 前端工程化

十四、Webpack 常用插件--自动清除输出目录
yarn add clean-webpack-plugin --dev


webpack.config.js

const {cleanWebpackPlugin} = require('clean-webpack-plugin')

module.exports = {
    plugins:[
        new cleanWebpackPlugin()
    ]
}


再次运行yarn webpack
十五、Webpack 自动生成HTML插件
yarn add html-webpack-plugin --dev


webpack.config.js

const {htmlWebpackPlugin} = require('html-webpack-plugin')

module.exports = {
    output:{
      //publicPath:'dist/'     这里需要注释掉这个路径
  },
    plugins:[
      //用于生成index.html文件
        new htmlWebpackPlugin({
            title:'设置标题',
            meta:{
                viewport:'width=device-width'
            },
            template:'./src/index.html',//指定模板文件
        }),
        // 用于生成about.html文件
      new htmlWebpackPlugin({
            filename:'about.html'
        }),
    ]
}



再次运行yarn webpack
十六、Webpack 常用插件 使用总结
yarn add copy-webpack-plugin --dev


webpack.config.js

const {copyWebpackPlugin} = require('copy-webpack-plugin')

module.exports = {
    plugins:[
        new copyWebpackPlugin([
          'plublic'
        ]),
    ]
}



再次运行yarn webpack
十七、Webpack 开发一个插件

相比于Loader,Plugin拥有更宽的能力范围,plugin通过钩子机制实现。
webpack 要求必须是一个函数或者一个包含apply方法的对象

class MyPlugin {
  apply(compiler) {
    console.log("MyPlugin");
    compiler.hook.emit.tap("MyPlugin", (compiler) => {
      // compilation 可以理解为此次打包的上下文
      for (const name in compilation.assets) {
        // console.log(name);
        console.log(compilation.assets[name].source());
        if (name.endsWith(".js")) {
          const contents = compilation.assets[name].source();
          const withoutComments = contents.replace(/\/\*\*+\*\//g, "");
          compilation.assets[name] = {
            source: () => withoutComments,
            size: () => widthoutComments.length,
          };
        }
      }
    });
  }
}

module.exports = {
  plugin: [new MyPlugin()],
};

十八、Webpack 自动编译
yarn webpack --watch
十九、Webpack 自动刷新浏览器
browser-sync dist --files "**/*"
二十、Webpack Dev Server

集成【自动编译】和【自动刷新浏览器】等功能

yarn add webpack-dev-server --dev

yarn webpack-dev-server --open   运行并且自动打开浏览器
二十一、Webpack Dev Server静态资源访问

contentBase 可以为webpack dev Server 额外的为开发服务器指定查找资源目录

webpack.config.js

module.exports = {
  devServer:{
    contentBase:['./plublic']
  }
};
二十二、Webpack Dev Server 代理API

webpack.config.js

module.exports = {
  devServer:{
    contentBase:['./plublic'],
    proxy:{
      '/api':{
        target:'https://api.github.com',
        pathRewrite:{
          '^/api':''
        },
         //不能使用localhost:8080作为请求github的主机名
        changeOrigin:true
      }
    }
  }
};

main.js

const ul = ducument.createElement('ul');
document.body.append(ul)

//跨域请求,虽然GitHub支持CORS,但不是每个服务端都应该支持
//fetch('https://api.github.com/users')
fetch('/api/users')
.then(res=>res.json())
.then(data=>{
  data.forEach(item=>{
    const li = document.createElement('li')
    li.textContent = item.login
    ul.append(li)
  })
})
二十三、Webpack Source Map 配置 (源代码地图)

webpack.config.js

module.exports = {
  devtool:'source-map'
};
二十四、Webpack eval模式的Source Map

webpack.config.js

module.exports = {
  devtool:'eval'
};

可以定位错误文件,不能定位具体行列信息

二十五、Webpack devtool 模式对比
  • eval 可以定位错误文件,不能定位具体行列信息,不生成.map文件
  • eval-source-map 可以定位错误出现的文件行和列信息,生成.map文件
  • cheap-eval-source-map 指定位到行 没有列的信息,生成.map文件,代码为转换es6过后的代码
  • cheap-module-eval-source-map 只定位到行 代码为源代码

  • eval - 是否使用eval执行模块代码
  • cheap - Source Map 是否包含行信息
  • module - 是否能够得到Loader 处理之前的源代码
二十六、Webpack 选择Source Map 模式

开发环境:cheap-moudule-eval-source-map

  • 每行代码不超过80个字符
  • 代码经过Loader 转换后的差异较大
  • 首次打包速度慢无所谓,重写打包相对较快

生产环境:none

  • Source Map 会暴露源代码
  • 调试是开发阶段的事情
    或者nosources-source-map:可以找到源代码错误位置,不至于暴露源码。
    理解不同模式的差异,适配不同的环境
二十七、Webpack 自动刷新的问题 开启HMR 模块热更新
  • 需求:自动刷新导致的页面状态丢失,在页面不刷新的前提下模块也可以及时更新

HMR是Webpack中最强大的功能之一,极大程度的提高了开发者的工作效率。
-Webpack 开启HMR
HMR集成在webpack-dev-server中,不需要单独安装模块

yarn webpack-dev-server --hot   开启热更新

也可以在配置文件中配置开启热更新

const webpack = require('webpack')
module.exports ={
  devServer:{
    hot:true
  },
  plugins:[
    new webpack.HotModuleReplacementPlugin()
  ]
}

yarn webpack-dev-server --open
二十八、Webpack HMR 的疑问
  • webpack 中的HMR并不可以开箱即用
    Webpack 中的HMR需要手动处理模块热替换逻辑
    现有的脚手架工具中已经包括了HMR逻辑
二十九、Webpack 使用HMR API

main.js

module.hot.accept('./editor',()=>{
  console.log('editor 模块更新了');
})
三十、Webpack HMR注意事项
  • 处理HMR的代码报错会导致自动刷新
hotOnly 不会使用自动刷新
devServer:{
  hotOnly:true
}
  • 没有启用HMR的情况下,HMR API报错
    先去判断
if(module.hot){
  ...
  ...
}
  • 代码找那个多了一些与业务无关的代码
    代码压缩后会自动去掉
三十一、Webpack 不同环境下的配置
  • 1、配置文件根据环境不同导出不同的配置

const webpack = require("webpack");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");

module.exports = (env, argv) => {
  const config = {
    mode: "development",
    entry: "./src/main.js",
    output: {
      filename: "js/bundle.js",
    },
    devtool: "cheap-eval-module-source-map",
    devServer: {
      hot: true,
      contentBase: "public",
    },
    module: {
      rulesL: [
        {
          test: /\.css$/,
          use: ["style-loader", "css-loader"],
        },
        {
          test: /\.(png|jpg?g|gif)$/,
          use: {
            loader: "file-loader",
            options: {
              outputPath: "img",
              name: "[name].[ext]",
            },
          },
        },
      ],
    },
    plugin: [
      new HtmlWebpackPlugin({
        title: "webpack title",
        template: "./src/index.html",
      }),
      new webpack.HotModuleReplacementPlugin(),
    ],
  };

  if (env === "production") {
    config.mode = "production";
    config.devtool = false;
    config.plugins = [
      ...config.plugins,
      new CleanWebpackPlugin(),
      new CopyWebpackPlugin(["plublic"]),
    ];
  }

  return config
};


// yarn webpack --env production
  • 2、一个环境一个配置文件
三十二、Webpack 不同环境的配置文件

webpack.common.js 公共的配置
webpack.dev.js 开发环境
webpack.prod.js 生产环境

安装yarn add webpack-merge --dev

例如webpack.prod.js

const common = require("./webpack.common");
const merge = require("webpack-merge");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");

module.exports = merge(common, {
  mode: "production",
  plugin: [new CleanWebpackPlugin(), new CopyWebpackPlugin(["plublic"])],
});

yarn webpack --config webpack.prod.js   //指定运行文件

也可以将命令构建到npm 模块
package.json -->scripts-->'build':'webpack --config webpack.prod.js'

运行yarn build
三十三、Webpack DefinePlugin

为我们的代码注入全局成员 process.env.NODE_ENV

const webpack = require("webpack");
module.exports = {
  mode: "none",
  entry: "./src/main.js",
  output: {
    filename: "bundle.js",
  },
  plugins: [
    new webpack.DefinePlugin({
      API_BASE_URL: JSON.stringify("https://api.example.com"),
    }),
  ],
};

三十四、Webpack 使用Tree Shaking

Tree Shaking 不是指某个配置选项,是一组功能搭配使用后的优化效果,会在生产模式下(production)自动启用

module.exports = {
  optimization: {
    usedExports: true,   //用来标记冗余代码
    minimize:true,   用于清除标记的冗余代码
  },
}


yarn webpack
三十五、Webpack 合并模块 concatenateModules

concatenateModules :尽可能的将所有的模块合并输出到一个函数中,即提升了运行效率,又减少了代码的体积。Scope Hoisting 作用域提升

三十六、Webpack Tree Shaking 与Babel

Tree Shaking 前提是ES Modules,由Webpack打包的代码必须使用ESM,为了转换代码中的ECMAScript新特性,会选择babel-loader处理js,ES Modules可能会转换成CommonJS,
在最新版本的babel-loader中自动帮我们关闭了ES M的转换插件。

三十七、Webpack sideEffects

标识代码是否有副作用,为Tree Shaking 提供更大的空间
副作用:模块执行时除了导出成员之外所做的事情
sideEffects 一般用于npm 包标记是否有副作用

optimization: {
    //开启功能
    sideEffects:true,
    // 模块指导处被使用的成员
    // usedExports: true,
    // 尽可能合并每一个模块到一个函数中
    // concatenateModules:true,
    // 压缩输出结果
    // minimize:true
  },


package.json

  "sideEffects":false,  //标记没有副作用


yarn webpack
三十八、Webpack sideEffects 注意

确保代码没有副作用
例如为Number的原型添加一个扩展方法 就是副作用的代码

Number.prototype.pad = function(size){
  //将数字转换为字符   1   2
   let result = this + ''
    //在数字前补指定个数的0    001  002
    while(result.length < size ){
        result = '0' + result
    }
    return result
}

在代码中载入的css 模块也属于副作用代码,解决办法 在package.json 中关掉副作用,或者标记当前文件那些是有副作用的,

package.json

有副作用的文件
  "sideEffects":[
      './src/extend.js',
      '*.css'
  ]

yarn  webpack
三十九、Webpack 代码分割 Code Splitting

代码分割的两种而方式

  • 一、Webpack 多入口打包
const webpack = require("webpack");
const {CleanWebpackPlugin} = require('Clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
  mode: "none",
  // entry: "./src/index.js",
  entry:{//多入口
    index:'./src/index.js',
    main:'./src/main.js'
  },
  output: {//多输出
    filename: "[name].bundle.js",
  },
  module:{
    rules:[
      {
        test:'/\.css$/',
        use:[
          'style-loader',
          'css-loader'
        ]
      }
    ]
  },
  optimization: {
    // sideEffects:true,
    // 模块指导处被使用的成员
    // usedExports: true,
    // 尽可能合并每一个模块到一个函数中
    // concatenateModules:true,
    // 压缩输出结果
    // minimize:true
  },
  plugins: [
    new webpack.DefinePlugin({
      API_BASE_URL: JSON.stringify("https://api.example.com"),
    }),
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title:'indexhtml',
      template:'./src/index.html',
      filename:'index.html',
      chunks:['index'],//指定需要引入的bundle
    }),
    new HtmlWebpackPlugin({
      title:'main.html',
      template:'./src/main.html',
      filename:'main.html',
      chunks:['main']//指定需要引入的bundle
    })
  ],
};

Webpack 提取公共模块
optimization: {
splitChunks:{
chunks:'all',
}
}

  • 二、Webpack 动态导入

// import post from './post/post'
// import album from './album/album'

const render = () => {
  const hash = window.location.hash || "#posts";
  const mainElement = document.querySelector("main");
  mainElement.innerHTML = "";

  if (hash === "#posts") {
    import("./posts/posts").then(({ default: posts }) => {
      mainElement.appendChild(posts());
    });
  } else if (hash === "#album") {
    import("./album/album").then(({ default: posts }) => {
      mainElement.appendChild(album());
    });
  }
};

render();

window.addEventListener("hashchange", render);

四十、Webpack 魔法注释Magic Comments
if (hash === "#posts") {
    import(/*webpackChunkName:'posts'*/"./posts/posts").then(({ default: posts }) => {
      mainElement.appendChild(posts());
    });
  } else if (hash === "#album") {
    import((/*webpackChunkName:'album'*/"./album/album").then(({ default: posts }) => {
      mainElement.appendChild(album());
    });
  }
四十一、Webpack MiniCssExtractPlugin 提取CSS到单文件
yarn add mini-css-extract-plugin --dev
四十二、Webpack OptimizeCssAssetsWebpackPlugin 压缩CSS插件
yarn add optimize-css-assets-webpack-plugin --dev


yarn add terser-webpack-plugin --dev

webpack.config.js


optimization: {
    minimizer: [
      new TerserWebpackPlugin(),
      new OptimizeCssAssetsWebpackPlugin(),
    ],
}
四十三、Webpack 输出文件名Hash
 output: {
    //多输出
    filename: "[name]-[contenthash:8].bundle.js",
    path: path.resolve(__dirname, "./dist"),
  },

可以指定位数,当前为8   不指定不写    :8  
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容