模块化与webpack

133 阅读4分钟

1-1.模块化

复杂的项目拆分成简单的模块

1-2.模块化需要解决的问题以及方案

问题:独立作用域;

早期方案(模块化之前的方案):

a.定义一个全局对象,将属性和方法挂载在全局对象上
b.立即执行函数: 将代码包裹到匿名函数中并调用,有了独立的函数作用域

模块化方案(模块化规范):

CommonJs规范(同步加载,无法实现异步按需加载,会导致运行卡死,不适用于浏览器,适用node运行环境):

module.exports = moduleA
exports.add = function(){}

require('./moduleA')
// 因为有缓存的功能,所以同一个模块多次导入,不会重复加载;
// 按照模块依赖关系,控制加载顺序
// 递归同步加载
AMD规范(浏览器运行环境):
define('模块名',[/*依赖模块列表*/], function(/*依赖模块导出结果*/){
    return module // 导出本模块的内容
})
require([/*依赖模块列表*/], function(/*依赖模块导出结果*/){
   // 依赖加载完成后的回调函数
})
需要引入第三方库支持(requirejs)
ES Mudule 规范:
导出:
export function hello(){}
export default {}
导入:
import {readFile} from 'fs'
import React from 'react'

三者的区别:

CommonJs:运行时加载,同步加载执行模块,能通过动态语句导出
AMD:运行时加载,优先异步加载依赖,在执行模块回调,能通过动态语句导出
ES6 Module:静态加载,编译时创建依赖引用,加载完即执行模块,不能通过动态语句导出,

2-1 webpack的介绍

将项目模块化时拆解的模块进行合并,只能用于采用了模块化开发的项目。

2-2 webpack的安装

安装环境:webpack5要求系统安装8.0.0及以上版本的Node

安装到全局的方式:npm i -g webpack webpack-cli

安装到本项目的方式:npm install --save-dev webpack webpack-cli

2-3 webpack的运行

在package.json 中配置scripts

  "scripts": {
    "build": "webpack"
  }

(1)命令行参数(优先级高)

  "scripts": {
    "build": "webpack --mode development"
  }

(2) 配置文件(更加灵活)(推荐)

webpack会默认读取项目根目录下(webpack.config.js)中的信息

module.exports = {
    mode: env, // 构建模式
    entry: './src/index.js', // 入口
    output: { // 输出
    },
    module: {
        rules: [{// loader配置

        }]
    },
    plugins: [ // plugin配置

    ],
    resolve: {// 模块解析配置

    },
    externals: { // 外部扩展配置

    }
}
mode: 构建时告知webpack使用相应模式的内置优化,
支持development(开发模式), production(生产模式),none(不使用任何默认优化选项)
entry: webpack以entry为入口,递归找出所有依赖的模块,构建bundle
entry值的类型:
单个入口: string | Array<string>
多个入口: {
    [ChunkName: string]: string | Array<string>
}
module:模块转换与模块解析由module设置

wepack原生支持模块:javascript,json,Assets,WebAssembly
非原生支持的模块:CoffeeScript,TypeScript,Sass,Less(需要loader转换)

module中rules属性的配置,决定如何转换不同类型的模块。
module:{
    rules: [{
        include: /src/
        exclude: /src\/add/
        test: /\.css$/
        resource: ''
    }]
}
include: 引入include中匹配的模块,与exclude和resource 不能同时设置
exclude: 排除exclude中匹配的模块,与include与resoucre不能同时设置
test: 模块匹配路径test,与resource不能同时设置
resource: 模块路径,与test不能同时设置

模块转化默认只支持javascript,所以对资源类型的转换,需要加type,用来设置原生支持的模块类型。与loader不能同时使用
示例: require('./a.jpg')
module: {
    rules: [{
        test: /\.jpg/,
        type: 'assets/resource'
    }]
}

loader: 将非原生模块转化为Javascript模块,可以使webpack支持多种语言和预处理器的语法编写模块。执行顺序: 从下到上,从右到左。
module: {
    rules: [{
        test: /\.css$/,
        exclude: /(node_modules)/,
        use: [
            {loader: 'css-loader'},
            {
                loader: 'postcss-loader',
                options: {
                    plugins: (loader) => [
                        require('autoprefixer')()
                    ]
                }
            }
        ]
    }]
}
resolve配置: 告诉webpack怎么查找模块
resolve: {// 模块解析配置
    extensions: ['.js', '.ts', '.css', '.scss', '.json'], // 如果require引入的文件没有加后缀名,则使用extensions中的先后顺序进行解析
    alias: {// 创建别名
        '@': './src'
    }
}
插件Plugins:
用于扩展webpack的功能,对于loader不能实现的扩展功能,可以使用webpack Plugin实现。
比如,将依赖的css模块打包到单独文件配置:
plugins:[
    new MiniCssExtractPlugin({
        // 配置编译后的css路径和文件名,相对于output里的path选项
        filename: 'css/[name].css'
    })
]
(Plugins配置项接收一个数组,数组中的每一项都是一个要使用的plugin的实例)
output:指示webpack如何去输出,以及在哪里输出你的[bundle/assets/其他使用webpack载入的任何内容]
output: {
    // 文件输出到哪个目录
    path: path.join(__dirname, '../src/dist')
    // 输出的合成文件的文件名
    filename: 'main.js',
    // 合成文件目标规范
    libraryTarget: 'umd',
    // 配合libraryTarget使用
    library: 'libraryName'
}

2-4 webpack 对不同模块规范的支持

1 CommonJS
导入:(同步加载依赖的模块)
var $ = require('jquery')
var myModule = require('my-module')
导出:
module.exports = function addTwo(a,b) { return a+b}
module.exports = function addThress(a,b,c) { return a+b+c}
动态加载(异步加载),require.ensure,接收4个参数,第一个是依赖的异步模块(数组),第二个是异步加载成功后执行的方法,第三个是异步加载失败时执行的方法,第四个是打包后的文件名
require.ensure(
    ['./add'],
    function callback(reuqire){
        var add = require('./add');
        var a = add(1,2);
        var b = sub(3,2);
        document.getElementById('show').innerText = '总和为:' + a + b
    },
    function errCallback(error) {
    },
    'chunk名称'
)
2 AMD
define 通过方法导出
define(
    ['模块名称'],
    ['依赖模块'],
    function factoryMethod(
        依赖模块的导出结果或者 requireexportsmodule
    ){
        依赖模块加载完成后调用此方法
    }
)
define({
    answer: 42
})
require( // AMD异步加载
    [依赖的模块列表, './add', './sub'],
    function(){
        依赖模块异步加载完成,会执行此函数
    }
)
3 ES6模块(推荐)
静态导入:
import MyMoudle from './my-module.js'
import { NamedExport } form './other-module.js'
导出:
export function Add(a,b) {
    return a+b
}
export default {}
动态加载:
import('loadsh').then(_ => {
    // 调用loadsh的方法
})
通过注释来设置chunk名称和加载模式(懒加载)
import(
     /*webpackChunkName: 'lodash_chunk'*/,
     /*webpackMode: 'lazy'*/,
    'loadsh'
).then(_ => {
    // 调用loadsh的方法
})