• 关于webpack的使用


    关于webpack

    webpakc的是模块打包器.而不是任务执行器。

    它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。
    webpack也提供了便捷的打包流程,项目构建,插件管理等等.
    为更好的构建项目,从开发到生产都一一提供了解决方案.
    Vue官方也推荐使用的vue-loader也是基于webpack的.

    WebPack和Grunt以及Gulp相比有什么特性

    Gulp/Grunt是一种能够优化前端的开发流程的工具,而WebPack是一种模块化的解决方案,不过Webpack的优点使得Webpack在很多场景下可以替代Gulp/Grunt类的工具。

    Grunt和Gulp的工作方式是:在一个配置文件中,指明对某些文件进行类似编译,组合,压缩等任务的具体步骤,工具之后可以自动替你完成这些任务。

    Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。

    Webpack的处理速度更快更直接,能打包更多不同类型的文件。

    一个常见的webpack配置文件


    webpack.config.js

    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'
        },
        output: {
          //publicPath:XXX 可以资金定义路径,build后输出的内容将会放入到这个路径当中
          filename: '[name]-[hash].js', //打包后输出文件的文件名,注意name用方括号括起来,name的值对应的为entry的key值,这样为了区分多个文件 
          path: path.resolve(__dirname, 'dist')//打包后的文件存放的地方(__dirname指向当前执行脚本所在的目录)
        },
        devtool: 'eval-source-map',//Source Maps提供了一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,也更容易调试,强调你只应该开发阶段使用它
        devServer: {//本地开发服务器,浏览器监听你的代码的修改,并自动刷新显示修改后的结果
          contentBase: './dist',//本地服务器所加载的页面所在的目录
          historyApiFallback: true, //不跳转,所有的跳转将指向index.html
          inline: true,//实时刷新
          hot: true
        },
        module: {//Loaders需要单独安装并且需要在webpack.config.js中的modules关键字下进行配置
         rules: [
           {
             test: /.css$/,//一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)
             use: [
                 {
                    loader: "style-loader"
                }, {
                    loader: "css-loader",
                    options: {
                        modules: true, //通过CSS模块,所有的类名,动画名默认都只作用于当前模块。 指定启用css modules,把CSS的类名传递到组件的代码中,这样做有效避免了全局污染
                        localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
                    }
             ]//css-loader使你能够使用类似@import 和 url(...)的方法实现 require()的功能,style-loader插件使对于CSS文件进行实时渲染到页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。
           },
           {
             test: /(.jsx|.js)$/,
             use: {
                loader: "babel-loader",//loader的名称(必须)
                options: {//为loaders提供额外的设置选项(可选)
                            presets: [
                                "env", "react"//解析Es6的babel-env-preset包和解析JSX的babel-preset-react包
                            ]
                        }
             },
             exclude: /node_modules/  //include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选);
            },
            {
             test: /.css$/,
             use: ExtractTextPlugin.extract({
                fallback: "style-loader",
                use: [{
                    loader: "css-loader",
                    options: {
                        modules: true,
                        localIdentName: '[name]__[local]--[hash:base64:5]'
                    }
                },
                {
                    loader: "postcss-loader"//自动添加适应不同浏览器的css前缀
                }],
             })
            }
           
         ]
        },
        plugins: [
          new CleanWebpackPlugin(['dist']),//清除dist目录
          new HtmlWebpackPlugin({ //依据一个简单的index.html模板,生成一个自动引用你打包后的JS文件的新index.html。
            title: 'Hot Module Replacement'
          }),
          new webpack.NamedModulesPlugin(),//添加该插件更容易观察依赖文件被更新
          new webpack.HotModuleReplacementPlugin(),//动态替换文件,热加载插件
          new webpack.BannerPlugin('版权所有,翻版必究'),//版本申请插件
          new webpack.optimize.CommonsChunkPlugin({
            name: 'common' // Specify the common bundle's name.
          }),
          new webpack.HashedModuleIdsPlugin(),//
          new webpack.optimize.OccurrenceOrderPlugin(),//为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的ID
          new webpack.optimize.UglifyJsPlugin(),//压缩JS代码
          new ExtractTextPlugin("style.css")//分离CSS和JS文件
        ],
        
      };

    package.json

    {
      "name": "webpack demo",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "start": "webpack",
        "server": "webpack-dev-server --open",
        "build": "NODE_ENV=production webpack --config ./webpack.prod.js --progress"
      },
      "author": "",
      "license": "ISC","dependencies": {

        "swiper": "^4.0.7",
        "vue": "^2.5.2",
        "vue-router": "^2.8.1",
        "element-ui": "2.2.1",

    "react": "^15.6.1",
         "react-dom": "^15.6.1"
      }
    }

    使用webpack

    1. 安装

    全局安装:

    //全局安装
    npm install -g webpack

    局部安装:

    //局部安装
    npm install --save-dev webpack

    2.工程结构

    可以根据项目的实际的情况设置目录。
    一般情况下可以设置为源码目录,生产目录。

    目录结构如下:

    3. 写一个webpack.config.js配置文件,首先是入口文件路径(entry)和打包后文件的存放路径(output)。

    module.exports = {
      entry:  __dirname + "/src/main.js",//已多次提及的唯一入口文件
      output: {
        path: __dirname + "/dist",//打包后的文件存放的地方
        filename: "bundle.js"//打包后输出文件的文件名
    }

    4. 对npm进行配置后可以在命令行中使用简单的npm start命令来执行打包任务。在package.json中对scripts对象进行相关设置即可,package.json中的script会按照一定顺序寻找命令对应位置,具体设置方法如下:

    {
      "name": "webpack-sample-project",
      "version": "1.0.0",
      "description": "Sample webpack project",
      "scripts": {
        "start": "webpack" 
      },
      "author": "zhang",
      "license": "ISC",
      "devDependencies": {
        "webpack": "3.10.0"
      }
    }

    注意:

    npm的start命令是一个特殊的脚本名称,其特殊性表现在,在命令行中使用npm start就可以执行其对于的命令。如果对应的此脚本名称不是start,想要在命令行中运行时,需要这样用npm run {script name},npm run build

    5. 开发工具

    webpack 提供了强大的开发工具

    webpack-server 提供了访问页面的服务

    webpack-watch 提供了观察文件的变化 

    1> source maps

    开发总是离不开调试,方便的调试能极大的提高开发效率,不过有时候通过打包后的文件,你是不容易找到出错了的地方,对应的你写的代码的位置的。而webpack可以在打包时为我们生成source maps,这为我们提供了一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,也更容易调试。

    webpack的配置文件中配置source maps,需要配置devtool。

    对小到中型的项目中,eval-source-map是一个很好的选项,再次强调,你只应该开发阶段使用它。

    module.exports = {
      devtool: 'eval-source-map',//source maps,便于调试
      entry:  __dirname + "/src/main.js",
      output: {
        path: __dirname + "/dist",
        filename: "bundle.js"
      }
    }

    2>使用webpack构建本地服务器

    为webpack打包生成的文件提供web服务,让你的浏览器监听你的代码的修改,并自动刷新显示修改后的结果,其实Webpack提供一个可选的本地开发服务器,这个本地服务器基于node.js构建,可以实现你想要的这些功能,不过它是一个单独的组件,在webpack中进行配置之前需要单独安装它作为项目依赖。

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

    webpack.config.js配置文件如下:

    module.exports = {
      devtool: 'eval-source-map',
    
      entry:  __dirname + "/src/main.js",
      output: {
        path: __dirname + "/dist",
        filename: "bundle.js"
      },
    
      devServer: {
        contentBase: "./dist",//设置本地服务器访问的基本目录
        historyApiFallback: true,//不跳转
        inline: true//实时刷新
      } 
    }

    package.json中的scripts对象中添加如下命令,用以开启本地服务器:

    "scripts": {
        "test": "echo "Error: no test specified" && exit 1",
        "start": "webpack",
        "server": "webpack-dev-server --open"
      },

    在终端中输入npm run server,即可在本地的8080端口查看结果

    6. Loaders

    Loaderswebpack提供的最激动人心的功能之一。通过使用不同的loaderwebpack有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分析转换scss为css,或者把下一代的JS文件(ES6,ES7)转换为现代浏览器兼容的JS文件,对React的开发而言,合适的Loaders可以把React的中用到的JSX文件转换为JS文件。
     
    Loaders需要单独安装并且需要在webpack.config.js中的modules关键字下进行配置。
     
    Loaders的配置包括以下几方面:
    • test:一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)
    • use: 进行转换时,应该使用哪个loader
    • loader:loader的名称(必须)
    • include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选);
    • query:为loaders提供额外的设置选项(可选)
    1> css
    webpack提供两个工具处理样式表,css-loader和 style-loader,二者处理的任务不同。
    css-loader使你能够使用类似@import和 url(...)的方法实现 require()的功能;
    style-loader将所有的计算后的样式加入页面中;
    二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。

    安装style-oader、css-loader,loader的作用就是将文件进行处理(编译,压缩)

    npm install --save-dev style-loader css-loader
    module.exports =     
    {
        module: {
          rules: [
            {
              test: /.css$/, //指定匹配文件,使用style-loader,css-loader
              use: [
                'style-loader',
                'css-loader'
              ]//引入的顺序至关重要,不可改变
          }]
         }
    }

    css预处理器

    Sass 和 Less 之类的预处理器是对原生CSS的拓展,它们允许你使用类似于variablesnestingmixinsinheritance等不存在于CSS中的特性来写CSS,CSS预处理器可以这些特殊类型的语句转化为浏览器可识别的CSS语句。

    以下是常用的CSS 处理loaders:

    • Less Loader
    • Sass Loader
    • Stylus Loader

    -PostCSS

    使用PostCSS来为CSS代码自动添加适应不同浏览器的CSS前缀。

    首先安装postcss-loader 和 autoprefixer(自动添加前缀的插件)

    npm install --save-dev postcss-loader autoprefixer

    在webpack配置文件中添加postcss-loader,在根目录新建postcss.config.js。

    //webpack.config.js
    module.exports = {
        ...
        module: {
            rules: [
                {
                    test: /.css$/,
                    use: [
                        {
                            loader: "style-loader"
                        }, {
                            loader: "css-loader",
                            options: {
                                modules: true
                            }
                        }, {
                            loader: "postcss-loader"
                        }
                    ]
                }
            ]
        }
    }

    postcss.config.js

    module.exports = {
        plugins: [
            require('autoprefixer')
        ]
    }

    2> babel

    Babel其实是一个编译JavaScript的平台,它可以编译代码帮你达到以下目的:

    • 让你能使用最新的JavaScript代码(ES6,ES7...),而不用管新标准是否被当前使用的浏览器完全支持;
    • 让你能使用基于JavaScript进行了拓展的语言,比如React的JSX;

    babel转化语法所需依赖项:

    babel-loader: 负责es6语法转化

    babel-core: babel核心包

    babel-preset-env: 告诉babel使用哪种转码规则进行文件处理

    先来一次性安装依赖包 

    // npm一次性安装多个依赖模块,模块之间用空格隔开
    npm install --save-dev babel-core babel-loader babel-preset-env babel-preset-react

    webpack中配置Babel的方法如下:

    module.exports = {
        ...
        module: {
            rules: [
                {
                    test: /(.jsx|.js)$/,
                    use: {
                        loader: "babel-loader",
                        options: {
                            presets: [
                                "env", "react"
                            ]
                        }
                    },
                    exclude: /node_modules/
                }
            ]
        }
    };
    Babel其实可以完全在 webpack.config.js中进行配置,但是考虑到babel具有非常多的配置选项,在单一的webpack.config.js文件中进行配置往往使得这个文件显得太复杂,因此一些开发者支持把babel的配置选项放在一个单独的名为 ".babelrc" 的配置文件中。我们现在的babel的配置并不算复杂,不过之后我们会再加一些东西,因此现在我们就提取出相关部分,分两个配置文件进行配置(webpack会自动调用.babelrc里的babel配置选项),如下:
     
    webpack.config.js
    module: {
            rules: [
                {
                    test: /(.jsx|.js)$/,
                    use: "babel-loader",
                    exclude: /node_modules/
                }
            ]
    }

    .babelrc

    {
      "presets": ["@babel/preset-env"]
    }

    3> Vue-Loader

    .vue 文件是一个自定义的文件类型,用类 HTML 语法描述一个 Vue 组件。每个 .vue 文件包含三种类型的顶级语言块 <template><script> 和 <style>,还允许添加可选的自定义块:

    <template>
      <div class="example">{{ msg }}</div>
    </template>
    
    <script>
    export default {
      data () {
        return {
          msg: 'Hello world!'
        }
      }
    }
    </script>
    
    <style>
    .example {
      color: red;
    }
    </style>
    
    <custom1>
      This could be e.g. documentation for the component.
    </custom1>

    vue-loader 会解析文件,提取每个语言块,如有必要会通过其它 loader 处理,最后将他们组装成一个 CommonJS 模块,module.exports 出一个 Vue.js 组件对象。

    vue-loader 支持使用非默认语言,比如 CSS 预处理器,预编译的 HTML 模版语言,通过设置语言块的 lang 属性。例如,你可以像下面这样使用 Sass 语法编写样式:

    <style lang="sass">
      /* write Sass! */
    </style>
    

    7. 插件(Plugins)

    插件(Plugins)用来拓展webpack功能,他们会在整个构建过程中生效,执行相关的任务。

    loaders是在打包构建过程中用来处理源文件的(JSX,Scss, Less),一次处理一个。

    plugins并不直接操作单个文件,是对整个构建过程起作用。

    使用插件的方法:

    使用某个插件,需要在webpack配置中的plugins关键字部分添加该插件的一个实例(plugins是一个数组)。

    例如:添加了一个给打包后代码添加版本声明的插件

    const webpack = require('webpack');
    
    module.exports = {
    ...
        module: {
            rules: [
                {
                    test: /(.jsx|.js)$/,
                    use: {
                        loader: "babel-loader"
                    },
                    exclude: /node_modules/
                },
                {
                    test: /.css$/,
                    use: [
                        {
                            loader: "style-loader"
                        }, {
                            loader: "css-loader",
                            options: {
                                modules: true
                            }
                        }, {
                            loader: "postcss-loader"
                        }
                    ]
                }
            ]
        },
        plugins: [
            new webpack.BannerPlugin('版权所有,翻版必究')
        ],
    };

    下面推荐几个常用的插件:

    HtmlWebpackPlugin

    依据一个简单的index.html模板,生成一个自动引用你打包后的JS文件的新index.html。这在每次生成的js文件名称不同时非常有用(比如添加了hash值)。

    安装:

    npm install --save-dev html-webpack-plugin

    webpack.config.js:

    const webpack = require('webpack');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
        ...
        plugins: [
            new webpack.BannerPlugin('版权所有,翻版必究'),
            new HtmlWebpackPlugin({
                template: __dirname + "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
            })
        ],
    };

    CleanWebpackPlugin

    清除目录的插件。

    添加了hash之后,会导致改变文件内容后重新打包时,文件名不同而内容越来越多,因此这里介绍另外一个很好用的插件clean-webpack-plugin

    引入clean-webpack-plugin插件后在配置文件的plugins中做相应配置即可:

    安装:

    npm install clean-webpack-plugin --save-dev 

    webpack.config.js:

    整个配置文件,注意插件的配置顺序.

      const HtmlWebpackPlugin = require('html-webpack-plugin');
      const CleanWebpackPlugin = require('clean-webpack-plugin');
    
      module.exports = {
        ...
        plugins: [
          new CleanWebpackPlugin(['dist']),//清除dist目录
          new HtmlWebpackPlugin({ //生成一个html页面指定其title,然后会自动将js文件生成引入js代码
            title: 'Output Management'
          })
        ]
      };

    Hot Module Replacement (HMR)

    主要是启动服务后文件可以进行热更新,例如css样式或者js等文件变更,会自动更新到页面上.只能用于开发环境不能用于生产环境.

    它允许你在修改组件代码后,自动刷新实时预览修改后的效果。

    在webpack中实现HMR也很简单,只需要做两项配置

    1. 在webpack配置文件中添加HMR插件;
    2. 在Webpack Dev Server中添加“hot”参数;

    webpack.config.js

    const webpack = require('webpack');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports
    = { ... devtool: 'eval-source-map', devServer: { contentBase: "./public",//本地服务器所加载的页面所在的目录 historyApiFallback: true,//不跳转 inline: true, hot: true }, ... plugins: [ new webpack.BannerPlugin('版权所有,翻版必究'), new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ template: __dirname + "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数 }), new webpack.NamedModulesPlugin(),//添加该插件更容易观察依赖文件被更新 new webpack.HotModuleReplacementPlugin()//热加载插件 ], };

    目前为止,我们已经使用webpack构建了一个完整的开发环境。

    产品阶段的构建

    在产品阶段,可能还需要对打包的文件进行额外的处理,比如说优化,压缩,缓存以及分离CSS和JS。

    对于复杂的项目来说,需要复杂的配置,这时候分解配置文件为多个小的文件可以使得事情井井有条。因此最好添加如下配置到配置文件的入口.

    process.env.NODE_ENV = 'production'

    webpack 配置可以提炼出来,使用webpack-merge将webpack的配置文件进行合并。

    我们可以建一个webpack.common.js文件,然后创建一个webpack.prod.js,webpack.dev.js,那么可以将prod,dev引入common共同引用参数.

    common文件

    const path = require('path');
     const CleanWebpackPlugin = require('clean-webpack-plugin');
     const HtmlWebpackPlugin = require('html-webpack-plugin');
    
     module.exports = {
       entry: {
         app: './src/index.js'
       },
       plugins: [
         new CleanWebpackPlugin(['dist']),
         new HtmlWebpackPlugin({
           title: 'Production'
         })
       ],
       output: {
         filename: '[name].bundle.js',
         path: path.resolve(__dirname, 'dist')
       }
     };

    生产环境:

    webpack提供了一些在发布阶段非常有用的优化插件,它们大多来自于webpack社区,可以通过npm安装,通过以下插件可以完成产品发布阶段所需的功能

    • OccurenceOrderPlugin:为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的ID
    • UglifyJsPlugin:压缩JS代码;
    • ExtractTextPlugin:分离CSS和JS文件(将css引入到js文件中,会一起打包成js文件)

    OccurenceOrder 和 UglifyJS plugins 都是内置插件,你需要做的只是安装其它非内置插件

    npm install --save-dev extract-text-webpack-plugin

    webpack.prod.js

     const merge = require('webpack-merge');
     const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
     const HtmlWebpackPlugin = require('html-webpack-plugin');
     const ExtractTextPlugin = require('extract-text-webpack-plugin');
     const common = require('./webpack.common.js');
    
     module.exports = merge(common, {
       module: {
            rules: [
                {
                    test: /(.jsx|.js)$/,
                    use: {
                        loader: "babel-loader"
                    },
                    exclude: /node_modules/
                },
                {
                    test: /.css$/,
                    use: [
                        {
                            loader: "style-loader"
                        }, {
                            loader: "css-loader",
                            options: {
                                modules: true
                            }
                        }, {
                            loader: "postcss-loader"
                        }
                    ]
                }
            ]
        },
       plugins: [
         new webpack.BannerPlugin('版权所有,翻版必究'),
         new HtmlWebpackPlugin({
                template: __dirname + "/app/index.tmpl.html"
         }),
         new webpack.optimize.OccurrenceOrderPlugin(),
         new webpack.optimize.UglifyJsPlugin(),
         new ExtractTextPlugin("style.css")
       ]
     });

    开发环境:

    const merge = require('webpack-merge');
    const common = require('./webpack.common.js');
    
    module.exports = merge(common, {
      devtool: 'inline-source-map',
       devServer: {
         contentBase: './dist'
       }
     });

     8. 代码管理

    默认的情况下,webpack会把引用的文件都会build到单独的文件当中.我们可能会把一些第三方的模块放到一个单独文件里.

    需要对配置文件做如下改动:

         //plugins数组当中添加一下参数
         new webpack.optimize.CommonsChunkPlugin({
           name: 'common' // Specify the common bundle's name.
         })

    9. 垫片

    当我们需要全局引入一个变量,我们可以使用ProvidePlugin。(webpack不推荐这样做,因为会影响到模块化编程)

    每个js文件就不需要去import引入对应的代码

    plugins: [
          new webpack.ProvidePlugin({
           _: 'lodash'
          })
    ]

    或许只需要部分第三方库的功能,我们可以这样进行配置

    plugins: [
          new webpack.ProvidePlugin({
           _: 'lodash',
           join:['lodash', 'join']//我们可以这样进行配置 ['module', 'child', 'child']
          })
    ]

    10. 缓存

    缓存无处不在,使用缓存的最好方法是保证你的文件名和文件内容是匹配的(内容改变,名称相应改变)

    webpack可以把一个哈希值添加到打包的文件名中,使用方法如下,添加特殊的字符串混合体([name], [id] and [hash])到输出文件名前。

    module.exports = {
    ..
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: "bundle-[hash].js"
        },
       ...
    };

    还有个问题webpack默认的hash是根据module.id以及内容生成,而module.id根据解析文件的顺序生成,一个重要的问题是每次改动引入的文件,就可能会造成其它文件hahs不一致.

    这时候就需要HashedModuleIdsPlugin插件,指定使用Path以及内容作为hash的内容.

    只需要配置文件当中在plugins增加如下配置:

    plugins:[
        ...,
        new webpack.HashedModuleIdsPlugin(),
        ...
    ]


     本文根据https://www.jianshu.com/p/42e11515c10f改写,加了一些自己的理解,希望对大家有帮助。

     

  • 相关阅读:
    JavaScript常见注意点(一)
    jspServlet2.5和Servlet3的区别
    jspMVC案例
    jQuery入口函数的写法
    Servlet 简介
    jspMVC设计模式和Servlet2.5入门案例
    display 属性
    JSON简单使用
    Tomcat修改端口号
    php开发环境简单配置
  • 原文地址:https://www.cnblogs.com/Super-scarlett/p/11085293.html
Copyright © 2020-2023  润新知