模块化开发与规范化标准

概述

1.模块化演变过程
2.模块化规范
3.常用的模块化打包工具
4.基于模块化工具构建现代web应用
5.打包工具的优化技巧

模块化演变过程

1.污染全局作用域
2.命名冲突问题
3.无法管理模块依赖关系

模块化规范的出现

1.CommonJS
一个文件就是一个模块
每个模块都有单独的作用域
通过module.exports导出成员
通过require函数载入模块
是以同步模式加载模块(在浏览器中 加载缓慢)
2.AMD(Asynchronous Module Definition) Require.js

ES Modules 特性

1.CommonJS
一个文件就是一个模块
每个模块都有单独的作用域
通过module.exports导出成员
通过require函数载入模块
是以同步模式加载模块(在浏览器中 加载缓慢)
2.AMD(Asynchronous Module Definition) Require.js

ES Modules 特性

1.自动采用严格模式,忽略’use strict’
2.每个ESM模块都是单独的私有作用域
3.ESM是通过cors去请求外部JS模块的(外模模块需要支持跨域)
4.ESM的script标签会延迟执行脚本(异步)

ES Modules 导出和导入

  export { foo } 导出一个成员引用 不是一个对象
  import { foo } from './*.js' 导入

  export default foo 默认导出 可以导出一个对象
  import abc from './*.js'  导入变量名可以变化

babel 兼容性方案

yarn add @babel/node @babel/core @babel/preset-env --dev
babel模块依赖一些核心模块 基于插件形式实现的
@babel/preset-env 是一个插件的集合 一个插件实现一个功能转换
yarn babel-node index.js --presets=@babel/preset-env
@babel/preset-env可以放在.babelrc配置 {"presets":["@babel/preset-env"]}
yarn babel-node index.js

一.Webpack 的构建流程主要有哪些环节?如果可以请尽可能详尽的描述 Webpack 打包的整个过程。

环节
1.初始化项目 安装webpack
2.配置文件webpack.config.js
3.配置项目入口(entry)、输出路径(output)、开发模式(mode)等
4.根据要加载的文件类型,安装配置不同的loader加载器
5.实现非loader的功能,安装相应的plugins
6.配置devserver 开启自动化编译和浏览器预览
7.配置optimization 进行项目优化操作
打包过程
1.执行打包命令(webpack)
2.通过webpack.config.js配置文件的entry入口配置开始查找项目依赖资源
3.根据配置的loader解析不同的资源,输出打包后的资源
4.在webpack打包过程中根据不同的环境配置不同plugin做一些额外的工作和一些优化操作
5.最后(output)输出Chunk文件 可多个或静态不变
plugin:主要是在webpack构建的不同阶段执行一些额外的工作,比如拷贝静态资源、清空打包后的文件夹等
1.通过钩子机制实现
2.plugin必须是一个函数或包含apply方法的对象
3.在方法体内通过webpack提供的API获取资源做响应处理
4.将处理完的资源通过webpack提供的方法返回该资源

class MyPlugin{
  // 传入相应的配置信息
  constructor(options){
  }

  // 将Compiler对象传递进来
  // 然后通过这个对象来注册钩子函数
  apply(compiler){
      // 注册emit钩子方法
      // compilation是一个上下文,
      compiler.hooks.emit.tap("MyPlugin",compilation=>{
          //在这里执行我们的方法来修改内容
        })
    }
}

二.webpack配置vue开发的流程

1.首先我们需要安装webpack webpack-cli到我们的开发依赖中
一阶段:在module.common.js文件 配置项目入口(entry)、输出路径(output)
二阶段:
    1.根据要加载的文件类型,安装配置不同的loader加载器
      因为我们项目是vue 所以我们需要安装vue-loader的依赖来对vue文件进行解析和转换
    2.接下来,由于我们项目中使用的是less,所以我们需要安装less-loader来对less进行解析和编译,同时,我们还需要安装css-loader和style-loader来对css文件和vue中的style标签进行解析
    注意:如果use是多个 顺序是从后到前执行 css解析需要style、css loader一起使用
    3.接下来是对js文件的解析,这里我们需要使用到babel-loader,将ES6+转为ES5
    注意:babel-loader内部会调用@babel/core 进行语法转换,@babel/core的转换依赖预设@babel/preset-env
    4.再然后是对图片文件的处理,我们使用url-loader来处理。由于url-loader是基于file-loader的
三阶段:
    1.因为是vue项目,需要使用一个VueLoaderPlugin插件引入
    2.此时我们发现没有html的解析文件,如果所以我们需要安装一个htm-webpack-plugin,用于指定html模板来生成我们的index.html,还可以设置title、meta等
      由于过程BASE_URL会报错所以我们需要使用webpack的DefinePlugin来定义一下BASE_URL的位置
四阶段:分配不同环境 通过webpack-merge 合并其他配置 类似Object.assign()
   1.开发环境的配置
     (1)mode模式分为三种 分别是 production、development 和 none 开发选择development
     (2)指定sourceMap 开发中方便找到源码中错误的位置
     (3)开发环境需要配置eslint 在保存时检查代码规范
     (4)配置devServer 启动热更新 提高开发效率 或使用代理服务 解决跨域
   1.生产环境的配置  
     (1)mode选择production
     (2)去除sourceMap 防止源码暴露
     (3)我们所有的代码都打包到了一个js文件中,接下来,我们首先把css样式文件分离出来,这一步我们需要使用到mini-css-extract-plugin,并且我们需要更改一下less和css文件的loader
     (4)我们需要做的就是对css文件和js文件的压缩和分包,并且通过webpack来去掉代码中的dubugger,console等,这一步我们需要使用optimize-css-assets-webpack-plugin 和 terser-webpack-plugin
最后:由于命令行的长度太长了,所以我们可以吧命令直接使用npm script的方式来执行,具体就是修改package.json里面的东西

webpack.common.js 代码演示

const path = require("path")
const VueLoaderPlugin = require("vue-loader/lib/plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")

// 公共的打包配置
module.exports = {
    // 入口文件 可以多个入口
    // entry: {
    //     index: './src/index.js',
    //     album: './src/album.js'
    // },
    entry: "./src/main.js",
    // 输出文件
    output: {
        // __dirname生成绝对路径
        path: path.resolve(__dirname, "dist")
    },
    module: {
        rules: [
            {
                // 解析vue文件
                test: /\.vue$/,
                use: ["vue-loader"]
            },
            {
                // 对js进行代码转译
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        // babel-loader内部会调用@babel/core 进行语法转换,@babel/core的转换依赖预设@babel/preset-env和插件plugin 
                        presets: ['@babel/preset-env']
                    }
                }
            },
            {
                // 对图片文件的处理 由于url-loader 是基于file-loader的,所以我们需要同时安装这个依赖
                test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
                use: {
                    loader: "url-loader",
                    // 限制图片大小 超过10k文件存储,不超过转base64
                    options: {
                        limit: 10 * 1024,
                        // 不加 src中会是一个object module
                        esModule: false
                    }
                }
            }
        ]
    },
    plugins: [
        new VueLoaderPlugin(),
        // 用于生成 index.html 多个HtmlWebpackPlugin可以生成不同*.html
        new HtmlWebpackPlugin({
            title: 'Webpack Plugin Sample',
            meta: {
                viewport: 'width=device-width'
            },
            template: "./public/index.html"
            // filename: 'index.html', //指定生成文件名
            // chunks: ['index']
        })
    ],

}

webpack.dev.js 代码演示

const { merge } = require("webpack-merge") //合并其他配置 类似Object.assign()
const commonConfig = require("./webpack.common")
const { DefinePlugin } = require("webpack")

// 开发环境的配置
module.exports = merge(commonConfig, {
    // 这个属性有三种取值,分别是 production、development 和 none。
    // 1. 生产模式下,Webpack 会自动优化打包结果;
    // 2. 开发模式下,Webpack 会自动优化打包速度,添加一些调试过程中的辅助;
    // 3. None 模式下,Webpack 就是运行最原始的打包,不做任何额外处理;
    mode: 'development',
    //指定sourceMap 开发中方便找到源码中错误的位置
    devtool: "#@cheap-module-source-map",
    module: {
        rules: [
            {

                test: /\.(vue|js)$/,
                loader: "eslint-loader",
                exclude: /node_modules/,
                // 预处理
                enforce: "pre"
            },
            {
                //如果use是多个 顺序是从后到前执行 css解析需要style、css loader一起使用
                test: /\.less$/,
                use: ["style-loader", "css-loader", "less-loader"]
            },
            {
                //这个是用来转换vue中的style标签和css文件的
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            },
        ]
    },
    devServer: {
        contentBase: './public', //指定服务器资源的根目录
        proxy: { //代理 本地解决跨域问题
            '/api': {
                // http://localhost:8080/api/users -> https://api.github.com/api/users
                target: 'https://api.github.com',
                // http://localhost:8080/api/users -> https://api.github.com/users
                pathRewrite: {
                    '^/api': ''
                },
                // 不能使用 localhost:8080 作为请求 GitHub 的主机名
                changeOrigin: true
            }
        },
        port: 9000,
        open: true,  //执行命令时自动打开页面
        hot: true //每次更新资源文件,不用更新所有资源,只更新部分,相当于加了一个补丁
    },
    plugins: [
        new DefinePlugin({
            //定义BASE_URL index.html中需要使用
            BASE_URL: "/public/"
        })
    ]
})

webpack.prod.js 代码演示

const { merge } = require("webpack-merge");
const commonConfig = require("./webpack.common");
const { DefinePlugin, LoaderOptionsPlugin } = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); //webpack4.X
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); 
const TerserPlugin = require("terser-webpack-plugin");
const path = require("path");

//生产环境的配置
module.exports = merge(commonConfig, {
    mode: "production",
    //去除sourceMap 防止源码暴露
    devtool: "none",

    //输出的文件名
    output: {
        filename: "js/[name].[hash:8].js",
        publicPath: "./"
    },
    //更改css和less的loader
    module: {
        rules: [
            {
                test: /\.less$/,
                use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"]
            },
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, "css-loader"]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./public/index.html",
            favicon: "./public/favicon.ico", // 在此处设置
            //对html代码进行压缩
            minify: {
                removeComments: true, //去注释
                collapseWhitespace: true, //压缩空格
                removeAttributeQuotes: true //去除属性引用
            }
        }),
        new DefinePlugin({
            BASE_URL: process.env.NODE_ENV
        }),
        //用于每次生成的时候,清理上次的打包文件
        new CleanWebpackPlugin(),
        //主要是为了抽离 css 样式,防止将样式打包在 js 中文件过大和因为文件大网络请求超时的情况 
        new MiniCssExtractPlugin({
            // hash/chunkhash解决缓存
            filename: "css/[name].[hash:7].css",
            chunkname: path.posix.join("static", "css/[id].[chunkhash:7].css")
        })
    ],
    optimization: {
        //代码分包 自动提取所有公共模块到单独包
        splitChunks: {
            chunks: "all"
        },
        minimize: true,
        minimizer: [
            //css压缩
            new OptimizeCSSAssetsPlugin({
                cssProcessorOptions: {
                    discardComments: { remove: true } //移除注释
                }
            }),
            //js优化
            new TerserPlugin({
                parallel: true, //开启多线程来提高构建速度
                sourceMap: false,
                terserOptions: {
                    warnings: false, //不展示warning
                    compress: {
                        unused: true, //去除未使用的
                        drop_debugger: true, //移除debugger
                        drop_console: true //去除console
                    },
                    output: {
                        comments: false //去除注释
                    }
                }
            })
        ]
    }
})

package.json

  "scripts": {
    "serve": "npx webpack-dev-server --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js",
    "lint": "eslint ./src"
  },

添加前缀

npm i postcss-loader autoprefixer --save-dev

2、在根目录新建一个postcss.config.js文件

module.exports ={
  plugins: [
    require('autoprefixer')
   ]
}
这就是对postCSS一个简单的配置,引入了autoprefixer插件。让postCSS拥有添加前缀的能力,增加相应的css3属性前缀

webpack 加载资源的方式

1.遵循 ES Modules 标准的 import 声明
2.遵循 CommonJS 标准的 require 函数
3.遵循 AMD 标准的 define 函数和 require 函数
4.样式代码中的 @import 执行和 url 函数
5.HTML 代码中图片标签的 src 属性

Webpack 代码分割

代码分包
所有代码最终都被打包到一起,bundle 体积过大
并不是每个模块在启动时都是必要的
模块打包是必要的,但是应用越来越大之后,需要进行分包,按需加载
有两种方式:多入口打包;ESM 动态导入
多入口打包
常用于多页应用程序
一个页面对应一个打包入口
公共部分单独提取
动态导入
按需加载,需要用到某个模块时,再加载这个模块
可以极大地节省带宽和流量
无需配置任何地方,只需要按照 ESM 动态导入的方式去导入模块,webpack 内部会自动处理分包和按需加载
使用单页应用开发框架(React/Vue),在项目中的路由映射组件就可以通过动态导入实现按需加载
Webpack 魔法注释
使用魔法注释可以为动态导入最终打包出来的文件命名
命名相同的模块最终会被打包到一起
Webpack 输出文件名 Hash
一般我们部署前端资源文件时,都会采用服务器的静态资源缓存
开启缓存的问题:缓存时间过短-效果不明显,缓存过期时间较长-应用发生了更新重新部署后客户端因为缓存得不到更新
解决上面问题,建议生产模式下,文件名使用 Hash,文件名不同也就是新的请求,解决了缓存的问题,服务器可以将缓存过期时间设置足够长
三种 Hash 方式
hash: 整个项目级别的,项目中任意一个地方改动,重新打包之后的 hash 值都会改变
chunkhash: chunk 级别的,同一路的打包 chunkhash 都是相同的
contenthash: 文件级别的hash,根据文件内容生成的hash值,不同的文件就有不同的值
解决缓存问题的最佳 hash 方式 [contenthash:8]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值