webpack官方文档学习

版权声明:所有原创文章未经本人同意不得随意转载,谢谢 https://blog.csdn.net/tangcc110/article/details/81948192

安装依赖的简写说明:以vue为例

cnpm i vue -S   // 生产依赖   等价于 --save 

cnpm i vue-loader css-loader vue-template-compiler -D  // 开发依赖   等价于--save-dev 

文档很长,我在前面把坑都踩了,希望大家看原文的时候结合我的文档来学习,要有耐心看哦。毕竟祖国的未来还是要靠你们去建设的^_^。

Step1.先学习概念,大概30分钟

Step2.学习指南,这个内容比较多,照着上面的示例慢慢练习

1.指南(太简单了,略)

2.安装(略)

3.起步(略)

4.管理资源(略)

5.管理输出,   HtmlWebpackPlugin 这里可能会遇到问题,那是因为没有在项目中安装webpack

clean-webpack-plugin 默认只能清除dist下的文件

引用图片:import src from "./g1.jpg"   不能直接src = "./g1.jpg";否则图片加载不了。

6.开发

         6.1 配置 devtool:"inline-source-map" ,会跳到错误的源文件中,助力开发调试

         6.2    "watch” :"webpack --watch"    //不用安装,改代码--刷新页面--成功

          6.3   1.webpack-dev-server 的使用,要安装,配置    

                  2.webpack.json配置:"dev":"webpack-dev-server --open" // cnpm run dev 就启动了

                  3. webpack.config.js配置: devServer:{ contentBase:"./dist" }

                    // 以上配置告知 webpack-dev-server,在 localhost:8080 下建立服务,将 dist 目录下的文件,作为可访问文件。

扫描二维码关注公众号,回复: 3592665 查看本文章

                    //选择了webpack-dev-server 就不要webpack-dev-middleware 和watch 了。

webpack-dev-server --hot // 也能起到模块热更新的作用

7.模块热替换,如下配置就可以自动更新了

          

以下几种loader也是热模块更新:

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

还有:Vue Loader 、angular HMR也会热替换

8.tree shaking,配置webpack.json文件,去除被打包文件中没有用到的代码。

//  8.1通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)

8.2 生产环境会自动压缩打包的代码,

      到这步的时候,webpack.json是这样的

{
  "name": "webpack0827",
  "sideEffects": false,
  "version": "1.0.0",
  "private": true,
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "watch": "webpack --watch",
    "dev": "webpack-dev-server --open"
  },
  "author": "tangcc",
  "license": "ISC",
  "dependencies": {
    "lodash": "^4.17.10"
  },
  "devDependencies": {
    "clean-webpack-plugin": "^0.1.19",
    "css-loader": "^1.0.0",
    "file-loader": "^2.0.0",
    "html-webpack-plugin": "^3.2.0",
    "style-loader": "^0.23.0",
    "webpack": "^4.17.1",
    "webpack-cli": "^3.1.0",
    "webpack-dev-server": "^3.1.6"
  }
}

webpack.config.js 是这样的

const webpack = require('webpack');
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
        // entry:"./src/index.js",
        entry:{
                app:"./src/index.js",
                // print:"./src/print-edit1.js"  // 做更新先干掉
        },
        // output:{
        //         filename :"bundle.js",
        //         path:path.resolve(__dirname,"dist")
        // },
        output:{
                path:path.resolve(__dirname,"dist")
                ,filename:'[name].bundle.js'
        }
        ,
        module:{
                rules:[
                        {
                                test: /.css$/,
                                use: [
                                        'style-loader',
                                        "css-loader"
                                ]
                        },
                        {
                        test:/\.(png|svg|jpg|gif)$/,
                                use:["file-loader"]
                        },
                        // {
                        //         include: path.resolve("node_modules", "lodash"),
                        //         sideEffects: false
                        // }
                ]
        }
        ,
        plugins :[
                new HtmlWebpackPlugin({
                        title: 'Output Management tnagcc'
                }),
                new CleanWebpackPlugin(["dist"])
                ,new webpack.NamedModulesPlugin() // 模块热替换
                ,new webpack.HotModuleReplacementPlugin() // 模块热替换
        ],
        devtool : "inline-source-map",
        devServer : { //告知 webpack-dev-server,在 localhost:8080 下建立服务,将 dist 目录下的文件,作为可访问文件。
                contentBase:"./dist"
                ,hot:true // 模块热替换开启,要配合plugins ,还得手动改一下index文件
        },
        mode: "production"// 生产环境会自动压缩代码,注意,--optimize-minimize 标记也会在 webpack 内部调用 UglifyJsPlugin。
}

9.生产环境构建,要拆分配置文件了,所以目录结构要改

npm install --save-dev webpack-merge

接下来配置文件也要分开写了,dev +base   or pro + base

webpack.common.js

const webpack = require('webpack');
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
        entry:{
                app:"./src/index.js",
        },
        output:{
                path:path.resolve(__dirname,"dist"),
                filename:"[name].bundle.js"
        },
        module:{
                rules:[
                        {
                                test:/.css$/,
                                use:["style-loader","css-loader"]
                        },
                        {
                                test:/\.(png|jpg|gif|svg)$/,
                                use:["file-loader"]
                        }
                ]
        },
        plugins:[
                new CleanWebpackPlugin(["dist"]) // 先清除dist后再去构建
                ,
                new HtmlWebpackPlugin({ // 会自动在dist目录构建一个包含所有依赖的html,默认名字叫index.html
                        title:"product environment",
                })
        ]

}

webpack.dev.js  ,这里文档里面是有个大坑的,不信你启动webpack.dev.js的时候,

报错了,应该如下:

const merge = require("webpack-merge"); // 用于合并配置,有很多其它功能
const common = require("./webpack.common.js")
module.exports = merge(common,{
        devtool:"inline-source-map", // 在运行时生成一个原文件便于调试的
        devServer:{
                contentBase:"./dist", // 告诉webpack-dev-server, 开户的调试用的本地服务localhost:8080 可用的本地目录是dist
                hot:true // 是否进行热更新,配合 plugins:[ new webpack.NamedModulesPlugin() // 给模块命名 ,new webpack.HotModuleReplacementPlugin() // 热模块更新]
        },
plugins:[
        new webpack.NamedModulesPlugin() // 模块热替换
        ,new webpack.HotModuleReplacementPlugin() // 模块热替换
]

})

快看图吧!心血呀,一步步的给你们踩坑 !^_^

webpack.prod.js

const merge = require("webpack-merge");
const UglifyJSPlugin  = require("uglifyjs-webpack-plugin")
const common = require("./webpack.common.js")
module.exports = merge(common,{
        plugins:[
                new UglifyJSPlugin()
        ]
})

重新配置一下NPM Scripts

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "build": "webpack --config webpack.prod.js",
  "watch": "webpack --watch",
  "dev": "webpack-dev-server --open --config webpack.dev.js"
},

优化:给生产环境也配置source map

// 我们鼓励你在生产环境中启用 source map,因为它们对调试源码(debug)和运行基准测试(benchmark tests)很有帮助。虽然有如此强大的功能,然而还是应该针对生成环境用途,选择一个构建快速的推荐配置(具体细节请查看 devtool)。对于本指南,我们将在生产环境中使用 source-map 选项,而不是我们在开发环境中用到的 inline-source-map

修改一下webpack.prod.js

const merge = require("webpack-merge");

const UglifyJSPlugin = require("uglifyjs-webpack-plugin")

const common = require("./webpack.common.js")

module.exports = merge(common,{

            devtool:"source-map", // 生产环境也要配置一下便于调试原码

            plugins:[ new UglifyJSPlugin({ sourceMap:true // }) ]

})

指定环境

许多 library 将通过与 process.env.NODE_ENV 环境变量关联,以决定 library 中应该引用哪些内容。例如,当不处于生产环境中时,某些 library 为了使调试变得容易,可能会添加额外的日志记录(log)和测试(test)。其实,当使用 process.env.NODE_ENV === 'production' 时,一些 library 可能针对具体用户的环境进行代码优化,从而删除或添加一些重要代码。我们可以使用 webpack 内置的 DefinePlugin 为所有的依赖定义这个变量:

webpack.prod.js 更改

const webpack = require("webpack")
const merge = require("webpack-merge");
const UglifyJSPlugin  = require("uglifyjs-webpack-plugin")
const common = require("./webpack.common.js")
module.exports = merge(common,{
        devtool:"source-map",
        plugins:[
                new UglifyJSPlugin({
                        sourceMap:true
                })
                ,new webpack.DefinePlugin({ // 改动
                        "process.env.NODE_ENV":JSON.stringify('production')
                })
        ]
})

CLI 替代选项(建议读一读哦)

以上描述也可以通过命令行实现。例如,--optimize-minimize 标记将在后台引用 UglifyJSPlugin。和以上描述的 DefinePlugin 实例相同,--define process.env.NODE_ENV="'production'" 也会做同样的事情。并且,webpack -p 将自动地调用上述这些标记,从而调用需要引入的插件。

这些简便方式虽然都很不错,但是我们通常建议只使用配置方式,因为在这两种场景中下,配置方式能够更好地帮助你了解自己正在做的事情。配置方式还可以让你更方便地控制这两个插件中的其他选项。

10.代码分离,去除各个模块代码重复的部分(用webpack自带的方法)

我们多个入口,默认打包出来的会包含重复的模块。如,两个文件都引用了jquery,则,打包出来两个文件里都包含了jquery.

src:里面两个用来测试的文件都引入一个相同的包如  import _ from "lodash"

输入输出不变:还是一般的多入口

entry:{
        app:"./src/index.js"
        // ,
        // another:'./src/another_module.js' // 多入口,也意味着多输出
},
output:{
        path:path.resolve(__dirname,"dist"),
        filename:"[name].bundle.js"
        //,
        // 决定非入口chunk的名称,动态导入的包就是一个非入口chunk,
        // 如 return import(/* webpackChunckName: lodash */"lodash").then(_=>{}).catch(e=>{})
        //chunkFilename:"[name].bundle.js"
}

webpack4+,不支持在plugins里配置

new webpack.optimize.CommonsChunkPlugin({
+       name: 'common' // 指定公共 bundle 的名称。
+     })

解决办法是在与plugins平级的地方:

optimization:{
        splitChunks: {
                cacheGroups: {
                        commons: {
                                test: /node_modules/,
                                name: "vendors",
                                chunks: "all"
                        }
                }
        },
        runtimeChunk:true
}

运行结果如下:

方法二:动态导入(dynamic imports),我只讲重点,其它的自己看官方文档

原理:import() 调用会在内部用到 promises。如果在旧有版本浏览器中使用 import(),记得使用 一个 polyfill 库(例如 es6-promise 或 promise-polyfill),来 shim Promise

请看index.js 新写法:

import "./style.css"

function component() { return import(/* webpackChunkName: "lodash" */"lodash" ).then(_=>{

          var element = document.createElement('div');

          element.innerHTML = _.join(["我是动态引入的" ],"lodash component 哦!");

          return element;

}).catch(error => "'An error occurred while loading the lodash component,haha tangcc'")

} component().then(element =>{ document.body.appendChild(element); })

非入口chunk的名称,:webpack.config.js 中的output配置一下

entry:{
        app:"./src/index.js"
        // ,
        // another:'./src/another_module.js' // 多入口,也意味着多输出
},
output:{
        path:path.resolve(__dirname,"dist"),
        filename:"[name].bundle.js"
        ,
        // 决定非入口chunk的名称,动态导入的包就是一个非入口chunk,
        // 如 return import(/* webpackChunckName: lodash */"lodash").then(_=>{}).catch(e=>{})
        chunkFilename:"[name].bundle.js"
},

结果:ok^_^(已测过)

这一小节还差分析检查模块就完结了,bundle 分析(bundle analysis) -- 我没有研究,先跳过去先。。。^_^

如果我们以分离代码作为开始,那么就以检查模块作为结束,分析输出结果是很有用处的。官方分析工具 是一个好的初始选择。下面是一些社区支持(community-supported)的可选工具:

  • webpack-chart: webpack 数据交互饼图。
  • webpack-visualizer: 可视化并分析你的 bundle,检查哪些模块占用空间,哪些可能是重复使用的。
  • webpack-bundle-analyzer: 一款分析 bundle 内容的插件及 CLI 工具,以便捷的、交互式、可缩放的树状图形式展现给用户。

11.懒加载(按需加载) ,当用户点击时才去加载对应的模块,难点在于如何去分模块(未研究)

新建print.js文件的时候,一定要“注意”当调用 ES6 模块的 import() 方法(引入模块)时,必须指向模块的 .default 值,因为它才是 promise 被处理后返回的实际的 module 对象。

print.js 

export default function printMe(){
        console.log("是谁点我的?print me")
}

index.js

import _ from "lodash" // 这是静态导入
import "./style.css"
function component(){
        var element = document.createElement('div');
        var button = document.createElement("button") ;
        button.innerHTML = "click me print ...";
        button.onclick = e => import(/* webpackChunkName:"print" */"./print-edit1.js").then(pExport => pExport.default()).catch(err =>{
                console.log("import component encounter some err")
        });
        element.appendChild(button);
        // return import(/* webpackChunkName: "lodash" */"lodash" ).then(_=>{
        //         var element = document.createElement('div');
        //         element.innerHTML = _.join(["我是动态引入的" ],"lodash component 哦!");
        //         return element;
        // }).catch(error => "'An error occurred while loading the lodash component,haha tangcc'")
        document.body.appendChild(element);
}
component();

配置不用改,有一点点问题就是 chunkFilename:"[name].bundle.js" 命名没有成功, 打包出来的名字竟然是用了id.bundle.js。

entry:{
        app:"./src/index.js"
        // ,
        // another:'./src/another_module.js' // 多入口,也意味着多输出
},
output:{
        path:path.resolve(__dirname,"dist"),
        filename:"[name].bundle.js"
        ,
        // 决定非入口chunk的名称,动态导入的包就是一个非入口chunk,
        // 如 return import(/* webpackChunckName: lodash */"lodash").then(_=>{}).catch(e=>{})
        chunkFilename:"[name].bundle.js"
},

又完成了一小节。

12.缓存(概念自己去看官网了^_^)

此指南的重点在于"通过必要的配置",以确保 webpack 编译生成的文件能够被客户端缓存,而在文件内容变化后,能够请求到新的文件。

输出文件的文件名(Output Filenames),用chunkhash命名

其实非常简单啦: filename:chunkhash

output:{
        path:path.resolve(__dirname,"dist")
        ,
        filename: "[name].[chunkhash].js"
},

提取模板(Extracting Boilerplate样板文件)

小于webpack4.0用以下两个配置可以实现公共代码的提取。

entry:{
        main:"./src/index.js",
        vendor:["lodash"]
},
plugins:[
        new CleanWebpackPlugin(["dist"]) // 先清除dist后再去构建
        ,
        new HtmlWebpackPlugin({ // 会自动在dist目录构建一个包含所有依赖的html,默认名字叫index.html
                title:"product environment",
        })
        ,
        // 代码分离时,去除重复模块,不用装 ,第10点 代码分离
        new webpack.optimize.CommonsChunkPlugin({ //  webpack4 废弃了
                name:'common' // 把两个入口重复的chunk提出来,以common.bundle.js的名字打包
        })
]

如果是webpack4.0会报以下错误了,

Error: webpack.optimize.CommonsChunkPlugin has been removed, please use config.optimization.splitChunks instead.

解决办法:

plugins:[ // 略
        new CleanWebpackPlugin(["dist"]) // 先清除dist后再去构建
        ,
        new HtmlWebpackPlugin({ // 会自动在dist目录构建一个包含所有依赖的html,默认名字叫index.html
                title:"product environment",
        })
]
,
// 关键代码
optimization:{ // 代码分离时,去除重复模块,不用装 ,第10点 代码分离// 把两个入口重复的chunk提出来,以vendors.bundle.js的名字打包
        splitChunks: {
                cacheGroups: {
                        commons: {
                                test: /node_modules/,
                                name: "vendors----",
                                chunks: "all"
                        }
                }
        },
        runtimeChunk:true
}

模块标识符(Module Identifiers)

主要是控制chunkhash的变化,理想状态下,只有相关文件改动,对应的chunkhash才能变

而 vendor没有改变但它 的 hash 发生变化,所以我们要处理。可以使用两个插件来解决这个问题。

第一个插件是 NamedModulesPlugin,将使用模块的路径,而不是数字标识符。虽然此插件有助于在开发过程中输出结果的可读性,然而执行时间会长一些。

第二个选择是使用 webpack.HashedModuleIdsPlugin(),推荐用于生产环境构建.

实际上,我用下面的配置,打包出的vendors文件的chunkhash也不会乱变。

optimization:{ // 代码分离时,去除重复模块,不用装 ,第10点 代码分离// 把两个入口重复的chunk提出来,以vendors.bundle.js的名字打包
        splitChunks: {
                cacheGroups: {
                        commons: {
                                test: /node_modules/,
                                name: "vendors----", //会提取出各模块的公共代码,自动命名按 output.chunkFilename 的规则 ,这个文件一般比较大>=70K。
                                chunks: "all"
                        }
                }
        },
        runtimeChunk:false
}

这一节过了^_^!

13.创建library(除了打包应用程序代码,webpack 还可以用于打包 JavaScript library--就是自己写一个js库发到npm上去

可能你看了官方的文档,还是不会操作呀,下面请按步骤。

1.按照文档把要发布的代码写好

2.给要发布的代码写一个README.md 文件,发布成功后这个文件会出现在你的包的描述说明中。

.md 文件的写法参考学习文档

https://www.cnblogs.com/liugang-vip/p/6337580.html

.md 文件的在线测试工具

http://tool.oschina.net/markdown/

3.进入项目目录,然后 npm publish (一定是npm   不要用淘宝的cnpm)

4.更新包(其实就是重新发布,在package.json )

       a.改:"version":"1.0.1" 或者  npm version 1.0.1

       b.npm publish

如果有报错请参考:

首先要设置一个版本号

npm社区版本号规则采用的是semver(语义化版本),主要规则版本格式:主版本号.次版本号.修订号,版本号递增规则如下:

  • 主版本号:当你做了不兼容的 API 修改,
  • 次版本号:当你做了向下兼容的功能性新增,
  • 修订号:当你做了向下兼容的问题修正。
    先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。
$ npm publish

这里有时候会遇到几个问题

问题1:

npm ERR! no_perms Private mode enable, only admin can publish this module:

这里注意的是因为国内网络问题,许多小伙伴把npm的镜像代理到淘宝或者别的地方了,这里要设置回原来的镜像。

$ npm config set registry=http://registry.npmjs.org

问题2:

npm ERR! you do not have permission to publish "your module name". Are you logged in as the correct user?

提示没有权限,其实就是你的module名npm上已经被占用啦,这时候你就去需要去npm搜索你的模块名称,如果搜索不到,就可以用,并且把package.json里的name修改过来,重新npm publish,看到如下信息就表示安装完成了,songpackage就是我的模块名。
作者:郝小淞
链接:https://www.jianshu.com/p/d9fb02a891d9
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

ok,过了!

猜你喜欢

转载自blog.csdn.net/tangcc110/article/details/81948192