webpack书本总结,入门webpack必备(1)

  • 观看了《webpack实践,进阶调优》的书本,记下了一下认为重要的笔记,可能对你也有用

正文开始:

commonjs与esmodule区别:

  • 1 commonjs模块依赖关系的建立发生在代码运行阶段; esmodule模块依赖关系的建立发生在代码编译阶段

​ - esmodule的优点

​ 1 esmodule可以“死代码检测和排除”通过静态分析可以在打包时去掉这些未曾使用过的模块,以减小打包资源体积。

​ 2 模块变量类型检查 ES6 Module的静态模块结构有助于确保模块之间传递的值或接口类型是正确的。

​ 3 编译器优化 在CommonJS等动态模块系统中,无论采用哪种方式,本质上导入的都是一个对象,而ES6 Module支持直接导入变 量,减少了引用层级,程序效率更高。

  • 2 值拷贝和动态映射

    commonjs得到的是一个值的拷贝,可以看做 const a = require(xx) = const a = module.exports。 a直接指向了exports这个对象. 的地址。如果是原始对象,那么源文件的值修改了,引用的引文的值并不会改变 因为他们是值的拷贝,并不是映射。

    而esmodule则是值的映射,并且映射是只读的。即使export const a = 1, import {a} from ‘x’,这里的a是对原🈶值的动态映射,也 就是当源文件a改变了,引入的这个a也会改变。但是不能在引入的文件

​ 对映射值更改,可以理解成一个笼子加一张网,透过网可以看到里面的实时景像,但是不能通过网去更改里面的东西。

  • 3 循环依赖

​ commonjs的循环依赖没有办法获取预期得到的结果。比如

// a.js
  const b = require('./b')
 console.log('b', b)
 module.exports = { a: 1 }
 // b.js
 const a = require('./a')
 console.log('a', a)
  module.exports = { b: 2 }
// index.js
  require('./a.')

正确的打印顺序应该是: ‘a’ : {}; ‘b’: {b:2}

原因就是:看webpack的require实现原理

       const cache = {}
       function require(moduleId){
           if(cache[moduleId]){
               // 已经加载过的直接到导出exports对象
               return cache[moduleId].exports
           }
           var module = {exports: {}}
           cache[moduleId] = module // 缓存
           // ..执行该模块的代码给module赋值
           // ...
          	return module.exports
      }

commonjs使用了cache缓存,当加载过的时候会将模块缓存起来,第一次index.js就已经加载了a文件。a文件加载b文件,控制权到b文件,此时又去加载a文件,cache已经有a了,直接返回module.exports 而此时a文件还没执行完毕,exports对象指向的是{},就导致这种结果了。

esmoudle循环加载呢?

​ 打印结果是: ‘a’, undefined ; ‘b’: {b:2}

​ esmodule还是没能获取正确的值。原因也很简单,打印a的时候,此时a文件还没执行完毕,export default默认导出undefine,所以打印了undefined。

​ 但是esmodule的特点是动态映射,也就是说,只要我们在打印a的时候,使用setTimeout延迟,此时b文件执行完毕,导出{b:2},然后a文件再导出{a:1},最后再打印a的时候,因为动态映射的关系就可以正确打印出来了。

区别三点:1 静态动态分析 2 值拷贝和值映射 3 循环引用的区别。

webpack 概念理解

  • entry: 入口

  • chunk: 每个entry入口文件,以及他所存在依赖关系的模块,都会在打包的时候封装成一个chunk代码块。

    在Webpack中可以理解成被抽象和包装过后的一些模块。它就像一个装着很多文件的文件袋,里面的文件就是各个模块,Webpack在外面加了一层包裹,从而形成了chunk。多个入口有多个chunk生成。(特殊情况一个入口也可以有多个chunk)

  • bundle: chunk代码块打包后的文件称为bundle

资源输入和输出

  • entry

    可以是 string | array | object | function

    • 数组的作用是将多个资源预先合并,如
    module.exports = {
    entry: ['babel-polyfill', 'c', './src/index.js'] ,
    };
    实际上是:
    module.exports = {
    entry: './src/index.js',
    };
    
    // index.js
    import 'c'
    import 'babel-polyfill';
    

    会在最后一个index.js作为入口,然后前面的相当于在index.js中引入。

    • 对象,可以定义多入口,属性名就是chunk名。如

      module.exports = {
      entry: {
      index: ['babel-polyfill', './src/index.js'],
      lib: './src/lib.js',
      },
      };
      
    • 函数可以动态配置一些东西,也可以返回一个promise进行异步操作。

      module.exports = {
      entry: () => new Promise((resolve) => {
      // 模拟异步操作
      setTimeout(() => {
      resolve('./src/index.js');
      }, 1000);
      }),
      };
      
    • 应用:单页面应用和多页面应用

      1 单页面:只需要一个入口文件,公共模块提取vendor

      module.exports = {
      context: path.join(__dirname, './src'),
      entry: {
      app: './src/app.js',
      vendor: ['react', 'react-dom', 'react-router'],
      },
      };
      

      将一些第三方模块一起打包,vendor并没有设置入口文件,可以设置CommonsChunkPlugin(4之后废弃),改用了optimization.splitChunks了。后续再做笔记。

      2 多页面

      module.exports = {
      entry: {
      pageA: './src/pageA.js',
      pageB: './src/pageB.js',
      pageC: './src/pageC.js',
      vendor: ['react', 'react-dom'] ,
      },
      };
      

      也是需要配置optimizaiotn.splitChunks。

      当第三方依赖较多时,我们可以用提取vendor的方法将这些模块打包到一个单独的 bundle中,以更有效地利用客户端缓存,加快页面渲染速度。

  • output出口

    1 filename: 多个assets需要指定不同文件名,[hash] [chunkhash] [id] [name]

    output: {
    filename: '[name].js',
    },
    

    这些变量用来: 1对chunk进行区分 2 控制客户端缓存,chunkhash/hash当chunk内容改变的时候改变,文件名跟着改变,用户下一次请求资源文件的时候,文件名改变导致只能重新下载新的版本,而不用本地缓存。

    一般用[name]@[chunkhash:8].js来进行命名,用于生产环境,开发环境则不需要。

    2: publicPath

    如果说path是用来指定资源的输出位置,那么publicPath就是用来指定资源的请求位置。

    一般用于三种情况

    1 html相关,可以指定publcikPath为html的相对路径,在请求的时候会以当前页面的index.html所在路径+相对路径,构成url,如
    值为 '', '.xx'或者以'.'开头的
    html地址:http://xx.com/app/index.html
    js文件名: a.js
    当publciPath: '', 实例的路径就是http:xx.com/app/a.js
    publicPath: './js', 世纪就是http:xx.com/app/js/a.js
    
    2 host相关,当值为'/'开头的,表示此时的publicPath以当前的host name为基础路径
    如
    publciPath: '/', 实际:http:xx.com/a.js
    publicPath: '/js/', 实际:http:xx.com/js/a.js
    
    3 cdn 绝对路径 publicPath以协议头或相对协议的形式
    publicPath: 'http://xx.cdn.com/', 实际就是http://.cdn.com/a.js
    

预处理器(loader)

loader赋予了webpack可处理不同资源类型的能力。

  • loader的本质:函数,webpack4之前,输入和输出都必须为字符串,4之后,loader也支持了抽象语法书AST的传递,从而减少重复的代码解析
output = loader(input)
// input可能是字符串,也可能是上一个loader的结果,包括转化后的字符串,sourcemap, AST对象。
// output也可能是转化后的字符串,sourcemap,ast对象。
module.exports = function loader (content, map, meta) {
var callback = this.async();
var result = handler(content, map, meta);
callback(
null, // error
result.content, // 转换后的内容
result.map, // 转换后的 source-map
result.meta, // 转换后的 AST
);
};

callback表示继续往下一个loader走,并传入对应参数。

  • loader options: loader作为预处理器通常会给开发者提供一些配置项,在引入loader的时候可以通过 options将它们传入
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
// css-loader 配置项
},
}
],
},
],
  • 更多配置:

    1 include & exclude 两者都存在,exclude权限更高

    2 resource与issuer,被加载模块和加载模块,exclude和include,test本质上属于对被加载者的配置。

    rules: [
    	{
    		test: /\.css$/,
    		use: ['style-loader', 'css-loader'],
    		exclude: /node_modules/,
    		issuer: {
    		test: /\.js$/,
    		include: /src/pages/,
          },
    	}
    ],
    

    对加载者的限制,只有src下的pages下的js目录加载了css文件,规则才会生效,才会使用css-loader处理。

    3 enforce:指定loader的种类,只接收pre post。

    loader按照执行顺序分为: pre, inline(官方不推荐), normal, post四种类型。正常定义的loader都是noraml类型,可以通过enforce执行pre和post.

    如对eslint-loader,就可以设置为p re,这样就可以在所有loader之前先进行代码检查。也可以不适用enforce而是自己保证loader的顺序是正确的即可。

    enforce可以强制指定loader的作用顺序,可读性更强。

  • Babel-loader

    babel-loader用来处理ES6+并将其编译为ES5,

    npm install babel-loader @babel/core @babel/preset-env
    

    babel-loader:它是使Babel与Webpack协同工作的模块 babel工作的环境。

    @babel/core:它是Babel编译器的核心模块, 用来转换代码。

    @babel/preset-env:它是Babel官方推荐的预置器,可根据用户设置的目标环境自动 添加所需的插件和补丁来编译ES6+代码,作用就是较core怎么转化。

    rules: [
    	{
    		test: /\.js$/,
    		exclude: /node_modules/,
    		use: {
    			loader: 'babel-loader',
    			options: {
    				cacheDirectory: true,
    				presets: [[
    				'env', {
    				modules: false,
    			}
    		]],
    		},
    		},
    	}
    ],
    

    cacheDirectory配置项启用缓存机制,可以接受一个路径,为true的时候,缓存目录为node_modules/.cache/babel-loader

    preset-env会默认将esModule打包成commons,modules为false表示不转化,用于后期tree-shaking。

    babel-loader支持从.babelrc文件读取Babel配置,因此可以将presets和plugins从 Webpack配置文件中提取出来,也能达到相同的效果。

  • ts-loader 他的配置项不在ts-loader中,而必须放入工程目录下的tsconfig.json中。

  • Html-load 将htmlt转为字符串,然后通过document.wirte写入到文档中。

  • file-loader默认以output的publicPath为路径,现在使用webpack5的asset了。

  • 自定义loader loader的实现就是一个函数,

    // loader.js
    module.exports = function(content) {
    if(this.cacheable){
    	this.cacheable() 
    }
    var useStrictPrefix = '\'use strict\';\n\n';
    return useStrictPrefix + content;
    }
    
    rules: [
    	{
    		test: /\.js$/,
    		use: {
    			loader: 'force-strict-loader',
    			options: {
    				sourceMap: true,
    				},
    		},
    }
    ],
    

    这样就完成了一个lo ader,他的作用是在打包出来的文件前面加上严格模式。webpack可以使用htis.cacheable控制缓存,当文件和其他依赖没有变化的时候,不应该重复进行转换工作。

    loader本质上是一个函数。第一个loader的输入是源文件,之后所有loader的输入是上 一个loader的输出,最后一个loader则直接输出给Webpack。

样式

  • PostCSS

    使用postcss-loader可以轻松地将PostCSS与Webpack连接起来。使用npm进行安装。

    module: {
    rules: [
    {
    test: /\.css/,
    use: [
    'style-loader',
    'css-loader',
    'postcss-loader',
    ] ,
    }
    ],
    },
    

    postcss要求有一个单独的配置文件,借助postcss-preset-env预设,自动添加前缀。配合stylelint检查样式

    const postcssPresetEnv = require("postcss-preset-env");
    const stylelint = require('stylelint');
    module.exports = {
      plugins: [
        postcssPresetEnv({
          browsers: "last 5 version",
        }),
        stylelint({
    			config: {
    			rules: {
    				'declaration-no-important': true,
    						},
    					},
    			})
      ],
    };
    
    
  • Css-module ,

    module: {
      rules: [
        {
          test: /\.css/,
          use: [
            "style-loader",
            {
              loader: "css-loader",
              options: {
                modules: true,
                localIdentName: "[name]__[local]__[hash:base64:5]",
              },
            },
          ],
        },
      ];
    }
    
    

    通过css-module开启modules开关,就可以启动模块化样式,localIdentName决定了转化后的类名,如

    // style.css
    .titke {
    	color: red;
    }
    

    转化后的类型就是style_title_xxxx [name]是chunk名字, [local]是原本选择器表示符号,[hash]是hash值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coderlin_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值