vue进阶01-webpack

1.webpack

1.1 webpack介绍

现今的很多网页其实可以看做是功能丰富的应用,它们拥有着复杂的JavaScript代码和一大堆依赖包。为了简化开发的复杂度,前端社区涌现出了很多好的实践方法

模块化,让我们可以把复杂的程序细化为小的文件;
类似于TypeScript这种在JavaScript基础上拓展的开发语言:使我们能够实现目前版本的JavaScript不能直接使用的特性,并且之后还能转换为JavaScript文件使浏览器可以识别;
Scss,less等CSS预处理器

这些改进确实大大的提高了我们的开发效率,但是利用它们开发的文件往往需要进行额外的处理才能让浏览器识别,而手动处理又是非常繁琐的,这就为WebPack类的工具的出现提供了需求。

1.2 webpack是什么

  • 打包工具
  • JavaScript 模块打包之后就可以运行在浏览器

1.3 webpack能做什么

  • webpack 可以当作是一个模块打包平台,但是它本身只能打包 JavaScript 模块
  • 对于其它的文件模块资源,则需要使用第三方 loader 来处理
  • JavaScript 资源打包
  • css 打包
  • 图片 打包
  • less
  • sass
  • babel EcmaScript 6 转 EcmaScript 5
  • 开发工具:http 服务器
  • 代码改变,自动刷新浏览器
  • 压缩代码
  • JavaScript 代码压缩
  • html 代码压缩
  • css 代码压缩

1.2 webpack相关网站

2. webpack入门

前置条件:学习 nodejs入门教程
安装webpack

npm install --global webpack webpack-cli

简写(-g等价于–global)

npm i -g webpack webpack-cli

安装到开发依赖–save-dev (-D等价)

npm i -D webpack webpack-cli

卸载(uninstall缩写un)

npm un webpack webpack-cli

查看webpack版本

webpack --version

准备目录结构

├── main.js
└── hello.js

hello.js内容:

hello="helloworld"
module.exports={
    say:hello
}

main.js内容:

var obj = require('./hello')
console.log(obj.say)

直接运行

node main.js
helloworld

webpack打包

# webpack 运行的js
webpack main.js

源代码目录多了一个dist目录生成了

├── main.js
└── hello.js
└── dist
   └── main.js

打开dist/main.js发现被混淆加密的js文件(包含main和被main引用的hello.js)
运行(效果和源码一致)

node node dist/main.js
helloworld

3. webpack命令

  1. webpack //对项目进行打包,默认读取webpack.config.js
  2. webpack --config //指定webpack配置文件 覆盖默认。
  3. webpack --watch // 自动监控当前项目下js文件的改变,改变自动打包。
  4. webpack --display-modules //打包时显示隐藏的模块
  5. webpack --display-chunks //打包时显示chunks 显示所有的被打包js文件
  6. webpack --display-error-details //显示详细错误信息
  7. webpack -w //提供watch方法,实时进行打包更新
  8. webpack -p //对打包后的文件进行压缩,默认会压缩
  9. webpack -d //提供SourceMaps,方便调试
  10. webpack --colors //输出结果带彩色,比如:会用红色显示耗时较长的步骤
  11. webpack --profile //输出性能数据,可以看到每一步的耗时
  12. webpack --progress //显示进度条
  13. webpack --hide-modules //隐藏所有的模块js信息 比如:
Entrypoint main = main.js
[0] ./main.js 50 bytes {0} [built]  添加--hide-modules 这些将被隐藏
[1] ./hello.js 54 bytes {0} [built]

4. idea配置vue支持

  1. 安装vue.js插件
  2. 设置支持es6语法 项目->File-> Settings->Languages & Frameworks->javascript 选择ECMAScripts6
  3. 新建 Static Web项目,执行命令npm init生成package.json
  4. 安装webpack npm install webpack --save-dev (开发环境需要依赖)
  "devDependencies": {
    "webpack": "^4.41.2"
  },

5. webpack配置文件

webpack默认读取项目根目录webpack.config.js文件。
新建webpack.config.js(webpack只能使用commonjs的require不能使用es6 import)

//import path from 'path'
path=require("path")
module.exports={
    //执行webpack 默认的主js文件
    entry: "./src/main.js",
    output: {
        //__dirname是当前webpack执行命令的目录
        path: path.join(__dirname,"./dist"),
        //[name]是entry对应文件名称 chunkhash是文件hash值
        filename: "[name].[chunkhash].js",
    }
}

将之前hello.js和main.js放在src(源代码)目录下。
执行命令(entry指定了打包的js文件)。

webpack

查看dist目录生成了文件

└── dist
   └── main.5f528dc392e9fc3581cd.js

如果多个资源进行打包

    entry: {
       main: "./src/main.js",
       test:"./src/test.js"
       别名:js路径
    },

如果某些第三方库不需要被打包:

externals: {
  'axios': 'axios',
  'html5-image-compress': 'html5ImgCompress',
  'iview': 'iview',
  'js-md5': 'md5',
  'moment': 'moment',
  'vue': 'Vue',
  'vue-i18n': 'VueI18n',
  'vue-router': 'VueRouter',
  'vuex': 'Vuex'
  '库名称':'导入模块功能'
},
也可以直接直接将整个库全部设置成外部导入(需要自己<script>引入对应库,webpack直接设置为window调用)
externals: [
    "vue"
]

比如在代码中引入:

import md5 from "js-md5";
import VueI18n from "vue-i18n";

5.1. webpack资源管理

webpack 不仅可以打包 JavaScript 模块,甚至它把网页开发中的一切资源都可以当作模块来打包处理。
按时它本身不知此,它只是一个打包平台,其它资源,例如 css、less、sass、img 等资源需要结合插件来实现,这些插件在 webpack 中被称之为 loader ,翻译过来就是 加载器 的意思。

5.1.1 CSS处理loader

打包 css 也是把 CSS 文件内容转换成了一个 JavaScript 模块,然后在运行 JavaScript 的时候,会动态的创建一个 style 节点插入到 head 头部。
官网css loader
中文指南css_loader
安装css-loader和style-loader

# css-loader 的作用是吧 CSS 文件转为 JavaScript 模块
# style-loader 的作用是动态创建 style 节点插入到 head 中
npm install --save-dev style-loader css-loader

同时如果需要loader需要安装

npm install --save-dev json5  emojis-list fast-json-stable-stringify

在src/styles新建一个a.css文件,随意添加一个样式

fontcolor{
    color:red;
}

main.js添加导入样式(es6语法)

import css from './styles/a.css';
import * as obj from './hello'
//var obj = require('./hello')
console.log(obj.say)
console.log(css.toString())

webpack新增一个modulerules

//import path from 'path'
path=require("path")
module.exports={
    //执行webpack 默认的主js文件
    entry: {
       main: "./src/main.js"
    },
    output: {
        //__dirname是当前webpack执行命令的目录
        path: path.join(__dirname,"./dist"),
        //[name]是entry对应文件名称 chunkhash是文件hash值
        filename: "[name].[chunkhash].js",
    },
    module: {
        rules: [{
            test:/.css$/,
            use:[
                'style-loader',
                'css-loader'
            ]
        }]
    }
}

执行webpack,
打开dist/main.[chunkhash].js文件搜索red,发现css代码生成在js文件中,当任意html加载了js文件自动将样式表嵌入一个style到dom中。

  • style-loader 负责找到dom元素插入样式表。
  • css-loader 负责将css文件内容写入到main.js文件中。
    需要dom元素才能执行。生成的main.js 无法使用node执行
node dist/main.656c78dbd267d57b2a3d.js
ReferenceError: document is not defined

可以直接将 css-loader 的结果作为字符串使用
安装to-string-loader

cnpm install to-string-loader --save-dev

修改webpack规则:

{
   test: /\.css$/,
   use: [
     'to-string-loader',
     'css-loader'
   ]
}

重新打包运行

node dist/main.5565593e3851f18ab52c.js
helloworld
fontcolor{
    color:red;
}

5.1.2 图片处理loader

file-loader默认情况下,将图片拷贝到dist目录生成的文件的文件名就是文件内容的 MD5 哈希值并会保留所引用资源的原始扩展名。
默认name :"[hash].[ext]",可以在选项中修改 ,详细参考
src下新增static/2.jpg
修改main.js,新增

import ig from './static/2.jpg'

webpack.config.js添加fileloader规则

{
            test:/(.jpg|.png)$/,
            use: {
                loader: "file-loader",
                options: {
                    name: "[name].[hash].[ext]"
                }
            }

        }

dist目录下生成了2.d6a71a3631e521154ca9545021735378.jpg

5.1.3 其他loader

其他loader如less-loader ,i18n-loader,urlloader,babel-loader 参考官网

5.1.4 自定义loader

loader可以理解是一个打包过滤器,当某个资源需要被打包处理时,调用loader进行源文件处理,比如less资源需要被构建成浏览器认识css,一般都在构建执行require(资源)或者import资源时触发过滤处理,返回的必须是javascript代码。

官网解释:

所谓 loader 只是一个导出为函数的 JavaScript 模块。loader runner 会调用这个函数,然后把上一个 loader 产生的结果或者资源文件(resource file)传入进去。函数的 this 上下文将由 webpack 填充,并且 loader runner 具有一些有用方法,可以使 loader 改变为异步调用方式,或者获取 query 参数。

第一个 loader 的传入参数只有一个:资源文件(resource file)的内容。compiler 需要得到最后一个 loader 产生的处理结果。这个处理结果应该是 String 或者 Buffer(被转换为一个 string),代表了模块的 JavaScript 源码。另外还可以传递一个可选的 SourceMap 结果(格式为 JSON 对象)。

如果是单个处理结果,可以在同步模式中直接返回。如果有多个处理结果,则必须调用 this.callback()。在异步模式中,必须调用 this.async(),来指示 loader runner 等待异步结果,它会返回 this.callback() 回调函数,随后 loader 必须返回 undefined 并且调用该回调函数。
参考教程:

中文开发教程 编写loader
src下新建 loader/txt-loader.js,代碼:

/*
*  实现一个 ${key} 替换对应值的功能
* */
loaderUtils=require("loader-utils")
//source就是某个匹配test规则的源文件内容,实现处理后返回被处理后的源码
module.exports=function(source){
    var options=loaderUtils.getOptions(this) || {}
    var resources=options["resources"]
    for(var key in resources){
        console.log(key,resources[key])
        source=source.replace("${"+key+"}",resources[key]);
    }
    source="var returnSource=\""+source+"\";console.log(returnSource)"
    return source
}

src下新建testloader.txt,內容:

hello 我是${name}

新建一個testloader.js作為entry 內容:

require("./testloader.txt")

webpack.config.js內容:

    entry: {
        //main: "./src/main.js",
        testloader: "./src/testloader.js"
    },
    //默认的loader都在node_modules 额外追加./loader目录
    resolveLoader: {
        modules: [path.join(__dirname,"./src/loader"),"node_modules"]
    },
    module: {
            rules: [{
                    test: /.txt$/,
                    exclude: /node_modules/,
                    use:[ {
                        loader:"txt-loader",
                        options: {
                            resources: {
                                name: "张三"
                            }
                        }}]
        
             }]
     }

webpack打包使用node命令運行打包後運行

webpack && node dist/testloader.js
hello 我是张三

5.2 输出管理

配置 output 选项可以控制 webpack 如何向硬盘写入编译文件。注意,即使可以存在多个入口起点,但只指定一个输出配置。

webpack 有着丰富的插件接口(rich plugin interface)。webpack 自身的多数功能都使用这个插件接口。这个插件接口使 webpack 变得极其灵活。插件

5.2.1 html-webpack-plugin

当你打包结束的时候,如果 index.html 在根目录直接运行的话,那么图片资源这些路径就无法访问到了。解决方案就是把 index.html 放到 dist 目录中。

但是 dist 是打包编译的结果,而非源码,所以把 index.html 放到 dist 就不合适。

而且你也会发现,我们打包的结果文件名:bundle.js ,如果一旦我把这个文件名给改了,则 index.html 也要手动修改。

综合以上我们遇到的问题,我们就可以使用一个插件:html-webpack-plugin 来解决。

安装依赖:

cnpm i -D html-webpack-plugin webpack-sources

src下新建index.html,添加内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<img src="static/2.jpg"/>
</body>
</html>

添加插件 html-webpack-plugin

    plugins: [
        new htmlWebpackPlugin({
            template:"./src/index.html"
        })
    ]

webpack命令打包
发现dist目录下拷贝了一个index.html并且多了一个script引用

<script type="text/javascript" src="main.7902354b0158b6bae327.js"></script></body>

但是图片缺并没有自动变成file-loader生成的图片
htmlloader可以将html中引用的图片替换,安装html-loader

 cnpm i -D html-loader

修改webpack.config.js

,{
            test: /\.html$/,
            use: [ "html-loader" ]
}

执行webpack,最终查看dist/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<img src="2.d6a71a3631e521154ca9545021735378.jpg"/>
<script type="text/javascript" src="main.7902354b0158b6bae327.js"></script></body>
</html>

5.2.2 extract-text-webpack-plugin

它会将所有的入口 chunk(entry chunks)中引用的 *.css,移动到独立分离的 CSS 文件。因此,你的样式将不再内嵌到 JS bundle 中,而是会放到一个单独的 CSS 文件(即 styles.css)当中。 如果你的样式文件大小较大,这会做更快提前加载,因为 CSS bundle 会跟 JS bundle 并行加载。官网
安装插件:

cnpm i -D  extract-text-webpack-plugin@next

注意extract-text-webpack-plugin不支持webpack4,应按照extract-text-webpack-plugin@next,
新版本名稱是:MiniCssExtractPlugin github地址:https://github.com/webpack-contrib/mini-css-extract-plugin
webpack.config添加配置:

ExtractTextPlugin=require("extract-text-webpack-plugin")

添加loader

{
    test:/.css$/,
    use: ExtractTextPlugin.extract({
        fallback: "style-loader",//use出現了錯誤使用fallback對應的loader
        use: "css-loader"
    })
}

添加插件

    plugins: [
        new htmlWebpackPlugin({
            template:"./src/index.html"
        }),
        new ExtractTextPlugin("styles.css")
    ]

5.2.3 clean-webpack-plugin

用於清除dist目錄下旧文件,保留新文件
安装插件

cnpm i -D clean-webpack-plugin

报错,需要引用一些其他模块

cnpm i -D fs.realpath minimatch brace-expansion concat-map balanced-match  path-is-absolute inflight

应用插件(注意代码内容没改 hash值是不变的,感觉像没删除一样)

const { CleanWebpackPlugin } = require("clean-webpack-plugin");
plugins: [
    new CleanWebpackPlugin({
        root:__dirname
    }),
    new htmlWebpackPlugin({
        template:"./src/index.html"
    }),
    new ExtractTextPlugin("styles.css")
]

5.2.4 webpack.optimize.CommonsChunkPlugin

ommonsChunkPlugin主要是用来提取第三方库和公共模块,避免首屏加载的bundle文件或者按需加载的bundle文件体积过大,从而导致加载时间过长,着实是优化的一把利器。

先来说一下各种教程以及文档中CommonsChunkPlugin提及到chunk有哪几种,主要有以下三种:

  1. webpack当中配置的入口文件(entry)是chunk,可以理解为entry chunk
  2. 入口文件以及它的依赖文件通过code split(代码分割)出来的也是chunk,可以理解为children chunk
  3. 通过CommonsChunkPlugin创建出来的文件也是chunk,可以理解为commons chunk

CommonsChunkPlugin可配置的属性

  • name:可以是已经存在的chunk(一般指入口文件)对应的name,那么就会把公共模块代码合并到这个chunk上;否则,会创建名字为name的commons chunk进行合并
  • filename:指定commons chunk的文件名
  • chunks:指定source chunk,即指定从哪些chunk当中去找公共模块,省略该选项的时候,默认就是entry chunks
  • minChunks:既可以是数字,也可以是函数,还可以是Infinity,具体用法和区别下面会说

children和async属于异步中的应用 。文章参考

几个概念搞清楚:

  1. webpack运行文件: 通过浏览器源码查看打包的js首字母就是webpackJsonp,这个是webpack基座,用于加载应用js使用,这个基座可以单独生产一个文件runtime.js。
  2. 自定义公共模块:比如某个日期处理js,多个entry使用,必然每个entry都会打包一份,完全可以提取出来一个公共的common.js。
  3. 第三方库:package.json依赖的第三方包。
  4. entry应用库:业务逻辑代码,一般main.js。

实战应用

开始配置:

   entry: {
    //entry应用库
    "main": "./src/main.js",
    "main2": "./src/main2.js",
    "main1": "./src/main1.js",
    "vendor": Object.keys(packageJson.dependencies)
  },
 plugins: [
        //entry中已经定义了vendor是获取packjson所有依赖,这里就是定义最终生成名称
        new webpack.optimize.CommonsChunkPlugin({
            name: 'vendor',
            filename: '[name].js'
        }),
        //从第三方库vendor.js抽离出公共部分也就是webpack基座,因为每个库都需要被webpack加载。
        new webpack.optimize.CommonsChunkPlugin({
            name: 'runtime',
            filename: '[name].js',
            chunks: ['vendor'] 
        }),
        //从main,main1,main2三个entrychunk中抽离公共部分common.js
        new webpack.optimize.CommonsChunkPlugin({
            name: 'common',
            filename: '[name].js',
            chunks: ['main','main1','main2']
        }),
    ]

5.2.5 webpack.EnvironmentPlugin

当webpack编译时,可通过process.env.NODE_ENV获取环境变量,但是应用js文件确不能使用,因为应用js是在页面浏览器加载时或者通过node 应用js才运行此时自然没有process.env.NODE_ENV,所有需要EnvironmentPlugin将webpack环境变量传递个应用js,可以直接使用。

安装插件(注意webpack版本不要超过4 否则会依赖n多包)

cnpm i -D webpack@3.6.0 webpack-cli neo-async enhanced-resolve watchpack @webassemblyjs/helper-code-frame @webassemblyjs/ast @xtuc/long @webassemblyjs/floating-point-hex-parser     @webassemblyjs/helper-fsm @webassemblyjs/helper-api-error @webassemblyjs/ieee754 @webassemblyjs/utf8 @webassemblyjs/leb128 @webassemblyjs/helper-wasm-bytecode @webassemblyjs/wasm-parser  locate-path is-extglob define-property is-plain-object assign-symbols regex-not split-string arr-flatten fill-range repeat-element snapdragon-node base object-visit map-visit ms source-map-resolve

webpack.config.js添加插件(注意生成代码没有"" 必须自己额外补一个)

 new Webpack.DefinePlugin({
            "process.env.PATH": "\""+process.env.PATH+"\""
        })

或者使用更简洁的(将process.env.PATH传递给应用js 省略process.env)

new Webpack.EnvironmentPlugin(['PATH'])

新建一个js打印环境变量

console.log(process.env.PATH)

添加一个webpack entry

entry: {
        main: "./src/main.js",
        envtest:"./src/envtest.js"
        //testloader: "./src/testloader.js"
    },

webpack打包访问 node ./dist/envtest.js 查看是否输出

5.2.5 Webpack.optimize.UglifyJsPlugin

该插件对js资源进行压缩,决定是否生成sourcemap,webpack4之后默认压缩。

        new Webpack.optimize.UglifyJsPlugin({
            sourceMap: true, //是否生成sourceMAp
            compress: {     //压缩
                warnings: false
            }
        })

6. webpack开发支持

每一次手动打包很麻烦,而且即便有 --watch 也不方便,还需要自己手动刷新浏览器。webpack 给你提供了一个工具:webpack-dev-server

它就可以实现监视代码改变,自动打包,打包完毕自动刷新浏览器的功能。

安装:

npm i -D webpack-dev-server

配置:

devServer: {  
  // 配置 webpack-dev-server 的 www 目录  
  contentBase: './dist'
},

配置 npm scritps:

"scripts": {  
 "dev": "webpack-dev-server --open"
 },

启动开发模式:

npm run dev

解释:该工具会自动帮你打包,打包完毕只有会自动开启一个服务器,默认监听 8080 端口号,同时自动打开浏览器让你访问,接下来就会自动监视代码的改变,然后自动编译,编译完毕,自动刷新浏览器。

7. 调试支持

7.1 nodejs调试

idea插件库搜索Nodejs插件安装 Nodejs 插件。
在这里插入图片描述
选择源代码js文件,下断点右键Debug即可(注意有require css动作不能调试必须webpack打包浏览器上使用sourcemap调试)。

在这里插入图片描述

7.2 chrome远程调试

sourcemap简介

关于webpack的sourcemap一共有7中配置,sourcemap 是指模块引入方式和源文件对应关系,不同的配置会影响打包的速度,和是否能追寻到源代码。

  1. eval 文档上解释的很明白,每个模块都封装到 eval 包裹起来,并在后面添加 //# sourceURL
  2. source-map 这是最原始的 source-map 实现方式,其实现是打包代码同时创建一个新的 sourcemap 文件, 并在打包文件的末尾添加 //# sourceURL 注释行告诉 JS 引擎文件在哪儿
  3. hidden-source-map 文档上也说了,就是 soucremap 但没注释,没注释怎么找文件呢?貌似只能靠后缀,譬如 xxx/bundle.js 文件,某些引擎会尝试去找 xxx/bundle.js.map
  4. inline-source-map 为每一个文件添加 sourcemap 的 DataUrl,注意这里的文件是打包前的每一个文件而不是最后打包出来的,同时这个 DataUrl 是包含一个文件完整 souremap 信息的 Base64 格式化后的字符串,而不是一个 url。
  5. eval-source-map 这个就是把 eval 的 sourceURL 换成了完整 souremap 信息的 DataUrl
  6. cheap-source-map 不包含列信息,不包含 loader 的 sourcemap,(譬如 babel 的 sourcemap)
  7. cheap-module-source-map 不包含列信息,同时 loader 的 sourcemap 也被简化为只包含对应行的。最终的 sourcemap 只有一份,它是 webpack 对 loader 生成的 sourcemap 进行简化,然后再次生成的。

webconfig开启生成sourcemap

module.exports={
    // devtool: 'cheap-module-source-map',
    devtool: "source-map",
    //执行webpack 默认的主js文件
    entry: {
        main: "./src/main.js",
        envtest:"./src/envtest.js"
        //testloader: "./src/testloader.js"
    },

比如我的index.html中引用了main.js(entry chunk)
index.htlm内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<img src="static/2.jpg"/>
</body>
</html>

main.js

import css from './styles/a.css';
import * as obj from './hello'
//var obj = require('./hello')
console.log(obj.say)
console.log(css.toString())

使用webpack-devserver运行程序 (参考6.0 webpack开发支持)

npm run dev

F12打开开发者工具集体
在这里插入图片描述
此时可以看到main.js源码就可以下断点调试了
下断点刷新调试:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值