webpack认知与项目实践

场景:项目发布时需要将根目录下的html文件和dist目录中所有文件打包上传,上线后迭代维护还需确保html代码中的资源文件正确。这样的做法非常麻烦,如果打包结果输出的目录或文件名发生变化,那么html代码中对应的script标签需要我们手动修改路径。

一、为什么需要webpack

前端项目的模块化开发 AMD, CommonJS , ES2015 import 等等方案,大多并不直接被浏览器支持,需要我们用 babel 将 jsx转换为js,将 scss,less转换为css;除了Js代码需要模块化,HTML和CSS这些资源文件也需要被模块化。随着项目越来越复杂、代码体积越来越大, 我们要找到优化,压缩,分割方案实现前端工程化。

webpack是一个前端模块化打包解决方案,又是一个可以融合运用各种前端新技术的平台,可以将零散的JS代码打包到一个JS文件中
对于环境兼容,webpack可以先编译转换,然后再进行打包
对于不同类型的前端模块,webpack支持在JS中以模块化的方式载入任意类型的资源文件;例如在js中加载css文件,被加载的css文件将会通过style标签的方式工作
还具备代码拆分能力,能够将应用中的所有模块按需分块打包;不用担心单个文件过大,导致加载慢的问题。

一些知名的脚手架工具,也大多基于webpack(比如create-react-app)

  • webpack在项目自动化构建方面的能力实现增强。
  • 实现打包之前自动清除上次打包的结果
  • 自动生成应用所需的html文件
  • 根据不同环境为代码注入类似API地址这种可能变化的部分
  • 拷贝不需要参与打包的资源文件到输出目录
  • 压缩webpack打包完成后输出的文件
  • 自动将打包结果发布到服务器实现自动部署

二、webpack为我们做哪些事

假设现在我们手中有一系列相互关联的文件js,jsx,css,less,jpg 

webpack实际上运行于node.js 其本身只能理解js文件

webpack默认会自动从src/index.js文件开始打包

//webpack.config.js 
const path = require("path") 
const {CleanWebpackPlugin} = require("clean-webpack-plugin") 
const HtmlWebpackPlugin = require("html-webpack-plugin") 
module.exports = { 
  entry:"./src/index.js", 
  output:{ 
    filename:"bundle.js", 
  }, 
  mode:"none", 
  plugins:[ 
    new CleanWebpackPlugin() 
    new HtmlWebpackPlugin({ 
      title:"hello webpack", 
      template:"./src/index.html" 
    })
    new CopyWebpackPlugin([ 
        "public"//需要拷贝的目录或者路径 
    ])
  ] 
} 

webpack核心 entry output  module  chunk  loader plugin

1. entry可以是三种值:字符串、 数组形式 、对象形式

entry:{
    main:'./src/index.js',
    second:'./src/index2.js',
    vendor: ['react','react-dom']
}

2.output 基本配置项

我们都另存过文件,当我们另存一个文件时,我们需要确定另存的文件名和另存的路径,webpack 将打包后的结果导出的过程就类似于此,此过程由 output 配置项控制,其最基本配置包括filename和path两项。这两项用以决定上述主js文件的存储行为

output:{

    path: path.join(__dirname,'./dist'),

    name:'js/bundle-[name]-[hash].js',

    chunkFilename:'js/[name].chunk.js',

    publicPath:'/dist/'

}

占位符有多种,常见的如下:

[name]:代表打包后文件的名称,在entry或代码中(之后会看到)确定;

[id]:webpack给块分配的内部chunk id,如果你没有隐藏,你能在打包后的命令行中看到;

[hash]:每次构建过程中,生成的唯一 hash 值;

[chunkhash]: 依据于打包生成文件内容的 hash 值,内容不变,值不变;

[ext]: 资源扩展名,如js,jsx,png等等;

3.module 的配置

对webpack而言,所有的文件都是模块,文件可以当做模块,而模块却不一定是一个独立的文件。

4.loader配置

loader用来识别特定文件供webpack进行转换后输出文件:babel-loader  postcss-loader   file-loader  url-loader style-loader  css-loader  sass-loader  vue-loader

5.plugins 的配置

plugin是补充一些webpack本来没有功能插件:HtmlWebpackPlugin、  cleanWebpackPlugin打包自动删除上次打包文件、静态资源拷贝copy-webpack-plugin插件

6.chunk

首页往往不需用到主js文件的所有代码,实际开发中我们常常使用一定方法对代码进行分割,方便按需加载,提升体验。这类不具备独立依赖的文件,我们称之为chunkfile。chunkfile的命名,在output中对应chunkFilename项。
通常,chunk 直接与 bundle 相对应,但是有些配置不会产生一对一的关系。
chunk(代码块)是一些模块 (module) 的封装单元。于 webpack 运行时的 seal 封包阶段生成,且直到资源构建阶段都会持续发生变化的代码块,在此期间插件通过各种钩子事件侵入各个编译阶段对 chunk 进行优化处理.

 

webpack4以后的版本支持零配置的方式直接启动打包,修改配置则在项目根目录下新建webpack.config.js文件作为webpack配置文件;
webpack.config.js运行在node.js环境,需要以commonJS的方式导入文件,导出成员,可以使用node.js内置模块

三、项目中最佳实践

 1.create-react-app 中 webpack 的配置
 

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
module.exports = {
    //devtool :此处的配置值为cheap-module-source-map,代表不带列映射的 SourceMap,将加载的 Source Map 简化为每行单独映射。
    devtool: 'cheap-module-source-map',
    //此处的entry是一个数组,代表着四项的代码都会添加到打包结果之中
    entry: [
        require.resolve('react-dev-utils/webpackHotDevClient'), //webpackHotDevClient可以被看做具有更好体验的WebpackDevServer。
        require.resolve('./polyfills'),//./ployfill.js用以在浏览器中支持 promise/fetch/object-assign。
        require.resolve('react-error-overlay'),//react-error-overlay在开发环境中使用,强制显示错误页面。
        'src/index.js'  //我们的app的主入口
    ],
    output: {
        path: '/build/',//path指定打包后文件存放的位置为/build/。
        pathinfo: true,//打包文件后在其中包含引用模块的信息,开发环境中有利于调试
        filename: 'static/js/bundle.js',//指定了打包的名字和基本的引用路径
        chunkFilename: 'static/js/[name].chunk.js',//指定了非入口文件的名称
        publicPath: '',//指定服务器读取时的路径
        devtoolModuleFilenameTemplate: info =>
            path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),  //这里是一个函数,指定了map位于磁盘的位置
    },
    resolve: {
        //modules指定了模块的搜索的位置,这里设置为node_modules
        modules: ['node_modules'],
        //extensions指明在引用模块时哪些后缀名可以忽略,这里忽略的文件名包括.js/.jsx/.web.js/.web.jsx等
        extensions: ['.web.js', '.js', '.json', '.web.jsx', '.jsx'],
        //alias创建 import 或 require 的别名,使得部分模块的引用变得简单
        alias: {
            'react-native': 'react-native-web',
        },
        //plugins:此处使用了 ModuleScopePlugin 的实例,用以限制自己编写的模块只能从src目录中引入。
        plugins: [new ModuleScopePlugin('/src')],
    },
    module: {
        strictExportPresence: true,//设置为 true,表明文件中如果缺少 exports 时会直接报错而不是警告
        rules: [
            //对 js/jsx 文件前置使用 eslintFormatter
            {
                test: /\.(js|jsx)$/,
                enforce: 'pre',
                use: [{
                    options: {
                        formatter: eslintFormatter,
                    },
                    loader: require.resolve('eslint-loader'),
                },],
                include: 'src',
            },

            //对exclude中的众多文件类型不使用file-loader,并设置其它文件打包后的名称按'static/media/[name].[hash:8].[ext]'格式设置。
            {
                exclude: [/\.html$/, /\.(js|jsx)$/, /\.css$/, /\.json$/, /\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
                loader: require.resolve('file-loader'),
                options: {
                    name: 'static/media/[name].[hash:8].[ext]',
                },
            },
            //对图片类型文件,调用url-loader进行处理
            {
                test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
                loader: require.resolve('url-loader'),
                options: {
                    limit: 10000,
                    name: 'static/media/[name].[hash:8].[ext]',
                },
            },
            // 对js/jsx文件调用babel-loader处理转换
            {
                test: /\.(js|jsx)$/,
                include: 'src',
                loader: require.resolve('babel-loader'),
                options: {
                    cacheDirectory: true,
                },
            },
            //对css文件,按顺序调用style-loader,css-loader,postcss-loader进行处理
            {
                test: /\.css$/,
                use: [
                    require.resolve('style-loader'),
                    {
                        loader: require.resolve('css-loader'),
                        options: {
                            importLoaders: 1,
                        },
                    },
                    {
                        loader: require.resolve('postcss-loader'),
                        options: {
                            ...
                    },
                    },
                ],
            },],
    },
    plugins: [
        //InterpolateHtmlPlugin和HtmlWebpackPlugin串行使用,允许在index.html中添加变量
        new InterpolateHtmlPlugin({
            NODE_ENV: 'development',
            PUBLIC_URL: ''
        }),
        //HtmlWebpackPlugin自动生成带有入口文件引用的index.html
        new HtmlWebpackPlugin({
            inject: true,
            template: 'public/index.html',
        }),
        //NamedModulesPlugin:当开启 HMR 的时候使用该插件会显示模块的相对路径,建议用于开发环境
        new webpack.NamedModulesPlugin(), new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: "development",
                PUBLIC_URL: '" "'
            }
        }),
        //HotModuleReplacementPlugin:启用模块热替换
        new webpack.HotModuleReplacementPlugin(),
        //CaseSensitivePathsPlugin:如果路径有误则直接报错。
        new CaseSensitivePathsPlugin(),
        //WatchMissingNodeModulesPlugin:此插件允许你安装库后自动重新构建打包文件
        new WatchMissingNodeModulesPlugin(paths.appNodeModules),
        new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),//忽略所匹配的moment.js
    ],
    //设置node的dgram/fs/let/tls模块的的值,如果在其它环境中使用时值为empty
    node: {
        dgram: 'empty',
        fs: 'empty',
        net: 'empty',
        tls: 'empty',
    },
    performance: {
        hints: false,//不提示测试环境的打包结果
    },
};

2.

你可能感兴趣的:(前端架构,webpack,前端,javascript)