跟着官网学习webpack

跟着官网学习webpack

声明:以下内容基本上都是对webpack官网案例进行学习后一些总结,方便自己后续进行复习,更详细的内容请查阅官网

webpack的安装

创建目录webpack-demo,并且进行npm的初始化

npm init -y // 生成package.json文件
npm install --save-dev webpack

如果你使用 webpack 4+ 版本,你还需要安装 CLI。
npm install --save-dev webpack-cli
请确保安装了 Node.js 的最新版本。使用 Node.js 最新的长期支持版本(LTS - Long Term Support),是理想的起步。使用旧版本,你可能遇到各种问题,因为它们可能缺少 webpack 功能以及/或者缺少相关 package 包*

我使用的node的版本
v14.17.1

起步案例

目录结构:
在这里插入图片描述
index.js文件

// 在index.js文件在index.html里面进行引入之前,还存在依赖lodash
// 提前将依赖打包进来
import _ from 'lodash';
function component() {
    var element = document.createElement('div');
  

    element.innerHTML = _.join(['Hello', 'webpack'], ' ');
  
    return element;
  }
  
  document.body.appendChild(component());

index.html:(在index.html中将打包后的文件引入)

<!doctype html>
<html>
  <head>
    <title>起步</title>

    <!-- <script src="https://unpkg.com/lodash@4.16.6"></script> -->
  </head>
  <body>
    <!-- script标签存在隐式的依赖关系,index.js文件执行之前,还依赖于script标签所引入的lodash -->
    <!-- <script src="./src/index.js"></script> -->

    <!-- 现在已经直接将lodash打包进去index.html -->
    <!-- npx webpack命令,可以将index.js作为入口起点,输出main.js命令 -->
    <!-- 直接打开index.html,查看到hello,webpack -->
    <script src="test.js"></script>
  </body>
</html>

webpack文件配置:

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'test.js',
        path: path.resolve(__dirname, 'dist')
    }
}

运行webpack.config.js文件的方式:
1: 使用cli:
npx webpack --config webpack.config.js
2: 配置package.json

"scripts": {
    "webpackTest": "webpack"
  },

启动:

npm run webpackTest

webpack管理资源(css文件,图片,字体文件等)

1:下载对应的loader
css文件对应的loader

npm install --save-dev style-loader css-loader

图片,字体对应的loader

npm install --save-dev file-loader

数据(xml等格式文件对应的loader):

npm install --save-dev xml-loader

管理输出(多打包入口,使用插件)

多增加一个print.js文件

export default function printMe() {
  console.log('I get called from print.js!');
}

修改原来的index.js文件:此时index.js文件中引用print.js文件

  import _ from 'lodash';
+ import printMe from './print.js';

  function component() {
    var element = document.createElement('div');
+   var btn = document.createElement('button');

    element.innerHTML = _.join(['Hello', 'webpack'], ' ');

+   btn.innerHTML = 'Click me and check the console!';
+   btn.onclick = printMe;
+
+   element.appendChild(btn);

    return element;
  }

  document.body.appendChild(component());

修改webpack.config.js(注意clear-webpack-plugin的用法和webpack官网的用法有差别)

// webpack的配置文件
const path = require('path');
// 当入口文件增多时,在index.html中的script标签手动引入生成的打包文件还是很麻烦
// 使用html-webpack-plugin插件可以解决
const HtmlWebpackPlugin = require('html-webpack-plugin');
const htmlPlugins = new HtmlWebpackPlugin();

// 每次打包都会在dist文件夹生成内容,当生成的内容过多,可以使用插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
    // entry: './src/index.js',
    entry: {
        app: './src/index.js',
        printer: './src/print.js'
    },
    output: {
        // filename: 'test.js',
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            }
        ]
    },
    plugins: [
        htmlPlugins,
        new CleanWebpackPlugin()
    ]
}

在开发中使用

1:source map 的使用:明确错误来源,不在生产环境中使用
在webpack.config.js中配置以下一句话即可:

devtool: 'inline-source-map',

2:代码发生变化后自动编译代码的三种方式:

方式1:在package.json文件中配置script脚本:
(webpack --watch)可以实现代码发生改变之后自动编译,但是浏览器要看到变化还是得手动刷新

"script":{
"watch": "webpack --watch“
}

方式2: webpack-dev-server(内容更新之后浏览器会自动刷新)访问在localhost:8080

npm install --save-dev webpack-dev-server

在webpack.config.js中加入以下配置(webpack官网未进行更新,contentBase写法已经被放弃,mode选项也是必须加上的):

devServer: {
        static: {  //服务器所加载文件的目录
            directory: path.join(__dirname, 'dist'),
        },
    },
 mode: 'development' // 设置mode

更改package.json,配置npm script

"webpackDevServer": "webpack-dev-server --open --mode development"

方式3: webpack-dev-middleware 访问在localhost:3000(省略)

模块热替换(Hot module raplacement HMR)

定义:允许在运行时更新各种模块,而无需完全进行刷新,只适用于开发阶段(官网的new webpack.NamedModulesPlugin()已经被默认配置,直接删去即可)

更改webpack.config.js,引入webpack包:

  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
  const CleanWebpackPlugin = require('clean-webpack-plugin');
+ const webpack = require('webpack');

  module.exports = {
    entry: {
+      app: './src/index.js'
    },
    devtool: 'inline-source-map',
    devServer: {
      contentBase: './dist',
+     hot: true
    },
    plugins: [
      new CleanWebpackPlugin(['dist']),
      new HtmlWebpackPlugin({
        title: 'Hot Module Replacement'
      }),
+     new webpack.HotModuleReplacementPlugin()
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };

print.js是嵌入到index.js中进行使用的,所以当print.js模块发生更改时应该让webpack接受更新新的模块

index.js加入以下代码:

// 在index.js文件在index.html里面进行引入之前,还存在依赖lodash
// 提前将依赖打包进来
import _ from 'lodash';
import './index.css';
import printMe from './print.js';
import printME from './print.js';

function component() {
    var element = document.createElement('div');
    var btn = document.createElement('button');

    element.innerHTML = _.join(['Hello', 'webpack'], ' ');
    element.classList.add('hello');

    // element.innerHTML必须得放在btn.innerHTMl前面,否则不显示
    btn.innerHTML = '点击我获取';
    btn.onclick = printME;
    element.appendChild(btn);
    
    
    return element;
  }
  
  // document.body.appendChild(component());
  let element = component();
  document.body.appendChild(element);

  // 修改 index.js 文件,以便当 print.js 内部发生变更时可以告诉 webpack 接受更新的模块。
  if(module.hot) {
    module.hot.accept('./print.js', function() {
      console.log('Accepting the updates printMe module!');
      // printMe();
      document.body.removeChild(element);
      element = component();
      document.body.appendChild(element);
    })
  }

完成上面的操作,点击的时候

此时当print.js发生改变时,页面自动更新:下面是浏览器控制台的输出:
在这里插入图片描述
HMR还可以用来修改样式表:
借助于 style-loader 的帮助,CSS 的模块热替换实际上是相当简单的。当更新 CSS 依赖模块时,此 loader 在后台使用 module.hot.accept 来修补(patch) style 标签。
所以,不需要任何配置,在更改css文件时,页面就能自动进行更新

tree shaking

tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)

新的 webpack 4 正式版本,扩展了这个检测能力,通过 package.json 的 “sideEffects” 属性作为标记,向 compiler 提供提示,表明项目中的哪些文件是 “pure(纯的 ES2015 模块)”,由此可以安全地删除文件中未使用的部分。

pageage.json的配置:

{
  "name": "your-project",
  // ,如果所有代码都不包含副作用,我们就可以简单地将该属性标记为 false
  // "sideEffects": false
  // 如果有代码存在副作用,将存在副作用的代码文件陈列
  "sideEffects": [
    "./src/some-side-effectful-file.js",
     "*.css"
  ]
}

在webpack.config.js中配置生产模式即可删除多余的代码:

+ mode: "production"

生产环境构建

开发环境和生产环境的构建目标差异很大:
开发环境:要求具有实时重载或者热模块替换能力的source map 或者localhost server
生产环境:要求体积更小的bundle
所以:提取公共的webpack配置,并且为生产环境和开发环境提供不同的webpack配置

将webpack.config.js文件拆分成三个文件:
webpack.common.js (公共的配置)

// webpack的配置文件
const path = require('path');
// 当入口文件增多时,在index.html中的script标签手动引入生成的打包文件还是很麻烦
// 使用html-webpack-plugin插件可以解决
const HtmlWebpackPlugin = require('html-webpack-plugin');
const htmlPlugins = new HtmlWebpackPlugin();

// 每次打包都会在dist文件夹生成内容,当生成的内容过多,可以使用插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');



module.exports = {
    // entry: './src/index.js',
    entry: {
        app: './src/index.js',
        // printer: './src/print.js'
    },
   
    output: {
        // filename: 'test.js',
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            }
        ]
    },
    plugins: [
        htmlPlugins,
        new CleanWebpackPlugin(),

    ],
   
}

webpack.dev.js


const { merge } = require('webpack-merge');
const webpack = require('webpack');
const common = require('./webpack.common.js');
const path = require('path');
module.exports = merge(common, {
   
    devServer: {
        static: {  //服务器所加载文件的目录
            directory: path.join(__dirname, 'dist'),
        },
        // 配置模块热替换
        hot: true
    },
    devtool: 'inline-source-map',
   
    plugins: [
        
        new webpack.HotModuleReplacementPlugin()

    ],
   
   
})

webpack.prod.js


+ const webpack = require('webpack');
  const {merge} = require('webpack-merge');
  const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
  const common = require('./webpack.common.js');

  module.exports = merge(common, {
    devtool: 'source-map',
    plugins: [
      new UglifyJSPlugin({
        sourceMap: true
-     })
+     }),
+     new webpack.DefinePlugin({
+       'process.env.NODE_ENV': JSON.stringify('production')
+     })
    ]
  });

这三个文件需要使用webpack-merge包才能进行合并

注意: react库中任何位于 /src 的本地代码都可以关联到 process.env.NODE_ENV 环境变量,所以以下检查也是有效的:`

+ if (process.env.NODE_ENV !== 'production') {
+   console.log('Looks like we are in development mode!');
+ }`

代码分离

三种代码分离的方式:
1: 入口起点:(多打包入口)

如果入口 chunks 之间包含重复的模块,那些重复模块都会被引入到各个 bundle 中。
这种方法不够灵活,并且不能将核心应用程序逻辑进行动态拆分代码

entry: {
    index: './src/index.js',
    another: './src/another-module.js'
  },

2:防止重复
(官网的CommonsChunkPlugin插件已经被废弃)
在webpack.config.js中使用以下的配置

// 提取公共的模块
    optimization: {
        splitChunks: {
             chunks: 'all',
            name: 'aa'
        }
     },

3:动态导入(ECMAScript提案中的import()语法)
去除掉上面两种方法对代码的改动,只需将index.js文件更改成下方:

// 动态导入分离chunk
function getComponent() {
  // import()返回一个promise
  // 注意,import()是动态导入,返回promise,与import语句导入是不相同的
  return import (/*webpackChunkName: "lodash" */ 'lodash').then( _ => {
    var element = document.createElement('div');
    element.innerHTML = _.join(['Hello', 'webpack'], ' ');
    return element
  }).catch( error => {
    return error
  })
}
getComponment().then( element => {
  document.body.appendChild(element)
})

运行结果:dist目录下面生成了lodash.bundle.js
在这里插入图片描述

懒加载

懒加载或者按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。

将index.js改造成点击button之后再加载print模块

import _ from 'lodash';
import './index.css';
// import printME from './print.js';
// math.js分别导出两个模块,但是index.js只是导入其中的一个模块
import { cube } from './math.js';

function component() {
    var element = document.createElement('div');
    var btn = document.createElement('button');
    var element = document.createElement('pre');

    element.innerHTML = _.join(['Hello', 'webpack'], ' ');
    element.classList.add('hello');

    
    element.innerHTML = [
          'Hello webpack!',
          '5 cubed is equal to ' + cube(5)
       ].join('\n\n');
    // element.innerHTML必须得放在btn.innerHTMl前面,否则不显示
    btn.innerHTML = '点击我获取';
    // btn.onclick = printME;
    // 让button在被点击的时候再进行动态加载
    btn.onclick = e => {
      // /* webpackChunkName: "print" */是给动态加载的模块进行命名
      // ./print是print路径
      import(/* webpackChunkName: "print" */ './print').then(module => {
        // 注意当调用 ES6 模块的 import() 方法(引入模块)时,必须指向模块的 .default 值,因为它才是 promise 被处理后返回的实际的 module 对象
        var print = module.default;
        print()
      })
    }
    element.appendChild(btn);
    
    
    return element;
  }

缓存

webpack 会生成一个可部署的 /dist 目录,然后把打包后的内容放置在此目录中。只要 /dist 目录中的内容部署到服务器上,客户端(通常是浏览器)就能够访问网站此服务器的网站及其资源。
获取资源是比较耗费时间的,这就是为什么浏览器使用一种名为 缓存 的技术。可以通过命中缓存,使访问速度更快。
然而,如果我们在部署新版本时不更改资源的文件名,浏览器可能会认为它没有被更新,就会使用它的缓存版本。
> 所以:更新了的文件在webpack重新打包时输出的模块名称不能和之前相同

确保模块依赖发生变化之后的输出模块名称和之前的不能相同:
使用【chunkhash】
配置webpack.config.js的output里面的filename一项:

 output: {
        // filename: '[name].bundle.js',
        // 为了保证更新文件之后webpack重新打包之后的模块名称和之前并不相同,可以在模块名称中使用chunkhash
        filename: '[name].[chunkhash].js',
        path: path.resolve(__dirname, 'dist'),
        chunkFilename: '[name].bundle.js'
    },

运行npm run webpack(webpack是我在package.json文件里面给脚本自定义的一个名称)
先后运行两次,发现模块名称中的hash值并没有发生变化(应该是webpack作出了改进,官网中是有变化的,需要进行CommonsChunkPlugin的配置)
在这里插入图片描述
更改index.js文件随便console.log一些数字,再次进行打包,打包的名称发生了变化
在这里插入图片描述

性能优化的操作:将第三方库(library)(例如 lodash 或 react)提取到单独的 vendor chunk 文件中,这是因为,它们很少像本地的源代码那样频繁修改。所以保证他们打包出来的模块的名称不变即可。

配置webpack.config.js

 entry: {
        app: './src/index.js',
        vender: [
            // 将一些基本不修改的第三方库单独提取出来,这样子就不收到其他的文件变化的影响
            'lodash'
        ]
    },

更改了index.js文件的内容之后,只是app模块的hash值发生了变化,vender这个第三方包的打包模块并没有收到影响
修改前:
在这里插入图片描述
修改后:
在这里插入图片描述

创建library(省略)

Shimming(细粒度)

一些传统的模块依赖的 this 指向的是 window 对象。当模块运行在 CommonJS 环境下这将会变成一个问题,也就是说此时的 this 指向的是 module.exports

  module: {
+     rules: [
+       {
+         test: require.resolve('index.js'),
+         use: 'imports-loader?this=>window'
+       }
+     ]
+   },

渐进式网络应用程序

渐进式网络应用程序(Progressive Web Application - PWA),是一种可以提供类似于原生应用程序(native app)体验的网络应用程序(web app)。PWA 可以用来做很多事。其中最重要的是,在离线(offline)时应用程序能够继续运行功能。这是通过使用名为 Service Workers 的网络技术来实现的。

TypeScript


> npm install --save-dev typescript ts-loader

配置tsconfig.json来支持jsx,并且将typescript编译到ES5

{
  "compilerOptions": {
    "outDir": "./dist/",
    "noImplicitAny": true,
    "sourceMap": true,
    "module": "es6",
    "target": "es5",
    "jsx": "react",
    "allowJs": true
  }
}

webpack.config.js的配置:

const path = require('path');

module.exports = {
  entry: './src/index.ts',
  // 溯源错误的具体位置
  devtool: 'inline-source-map',
  module: {
    rules: [
    // 配置解析tsx文件
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/
      }
    ]
  },
  resolve: {
    extensions: [ '.tsx', '.ts', '.js' ]
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值