从开发文件到生产文件

 
有一天我突然意识到一个问题,在使用react框架搭建应用时,我使用到了sass/less,JSX模版以及ES6的语法在编辑器下进行开发,使用这些写法是可以提高开发的效率。可是浏览器它本身是并不能够“理解”这些语法的呀。就像下面这张图:
 
 
 
在开发代码文件 --> 生产代码文件的转换过程中,我们到底需要做些什么呢?没错,这一切都和webpack(或gulp)有关:
 
 
 
转一张webpack官网的图,webpack能把less/sass文件,json文件,乃至css文件,全都打包成js文件和静态资源文件(图片)
 

webpack和gulp的共同作用及两者的区别
 
webpack和gulp本质上并不是同一类型工具,但它们都能完成以下任务:

webpack:一个模块化工具(a module bundle)
gulp:一个任务运行器(a Task Runner)
 
在用react/vue/angular搭建单页面应用时,我们可以用webpack代替gulp的工作,方便而快捷。两者具体的区别,在这里不多赘述,大家自行查阅资料。下面我主要介绍一下webpack的使用
 
除了利用webpack实现开发代码 --> 生产代码的转换,我们为什么要用它做其他一些工作,比如文件打包(文件合并),JS/css压缩呢?
 
为什么要用webpack实现文件打包?
 
为什么我们要做文件打包的工作,这样做有什么意义吗?这要从我们曾经喜闻乐见的<script>标签说起。对于许多初学者来说,在每个HTML页面里写入大量的<script>标签是再正常不过的事情。然后在部署上线时就会生成这样的HTML文件
 
<html>
<body>
<script src = 'http:// ... a.js' />
<script src = 'http:// ... b.js' />
<script src = 'http:// ... c.js' />
<script src = 'http:// ... d.js' />
</body>
</html>
 
咋看一下似乎也没什么不对,但是仔细想想,每个页面都发起如此多的http请求,大量的页面叠加在一起,这将极大降低页面的性能,使页面加载得很慢。那么我们想,能不能将无数个script文件合为一个(或几个)文件,这样请求数不就大大减少了吗?没错,webpack打包做的就是这样的作用
 
为什么要用webpack实现JS压缩?
 
和打包一样,压缩文件也是为了提高页面性能,(大家可结合自己对那些打开极慢的网站的体验感受一下页面性能的重要性)。使用webpack压缩文件时,它会做以下操作:
  • 删除注释
  • 删除空格 (所以我们偶尔会看到没有间隔或只有一行的JS代码)
  • 缩短变量名,函数名和函数参数名(var myName = '彭湖湾')-->var  a = '彭湖湾'

这样做的好处:

  • 减少文件体积,加快传输速度,提高页面性能
  • 实现代码混淆,破坏其可读性,保护创作者的知识产权
 (注:这一过程不可逆!需要事先做好备份工作)
为什么要用webpack实现sass,less的编译和JSX模版文件的转换?
 
也就是上文提到的,通过webpack的转换,从浏览器无法“理解”的开发代码生成一份浏览器能够“理解”的生产代码
 
commonJS和AMD规范
 
从大量<script>的写法到webpack的广泛使用,实际上就是前端模块化发展的过程,而其间有两个主要的模块化标准commonJS和AMD,webpack是基于commonJS的,(当然也兼容写AMD,不过不推荐)下面是commonJS 的模块写法:
 
const moduleInput = require('moduleInpu')
//输入模块
module.exports = {
//输出模块
  ...
}
 
下面我就一一来介绍如何用webpack实现上述三种功能:
 
首先你得创建一个文件webpackTest,在终端进入目录,写入$ npm install webpack -g,安装成功
 
1文件打包(SPA-单页面应用程序)
 
1-1安装好webpack后创建这样一个目录:
 
 
1-2:向component各文件和dist/index.html文件写入内容
 
dist表示的是生产目录,component是开发目录,我们平时开发时只在component目录下完成。dist/index.html是我们手动创建的,内容如下:

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<script type="text/javascript" src="bundle.js"></script></body>
</html>
我们希望通过webpack的文件打包,将component中的所有文件合并到dist/ab.js中来然后dist/index.html就可以引用dist/ab.js文件了。
component/a.js内容:
console.log('我是a.js文件');
component/b.js内容:
console.log("我是b.js文件");
component/ab.js内容:
require('./a')
require('./b')
console.log('我是ab.js,我require了a.js文件和b.js文件');
 
1-3向webpack.config.js中写入内容:
 
var path = require('path')

module.exports = {
entry:{
ab:'./component/ab.js',
},
output:{
filename:'bundle.js',
path:path.resolve(__dirname,'dist'),
},
}
webpack要求webpack.config.js的输出模块为一个对象,且包含两大基本属性:entry和output。entry,顾名思义,入口文件,上面代码表示component/ab.js是入口文件,output/bundle.js是输出文件。由于component/ab.js引入(require)了a.js文件和b.js文件,这三个文件会被一起打包dist/bundle.js中,(注:entry中可以写入相对路径)
 
var path = require('path')
path.resolve(__dirname,'dist')
 
 
这段代码什么意思?path是node的内置模块,resolve是它的一个方法,__dirname表示当前目录在磁盘中的绝对路径,path.resolve(__dirname,'dist') = __dirname + '/dist' ,在我的mac里它相当于Users/penghuwan/myprogram/webpackTest/dist(注意:必须为绝对路径,不能为相对路径!)
 
1- 4 OK!该写的都写好了,接下来,在终端进入目录,写入webpack回车
 
 
component下的三个文件都被打包好了,再回来看看我们的目录
 

多了一个dist/bundle.js的文件!让我们看看里面有什么:
 

就是我们独立写的a.js,b.js和ab.js打包后的dist/ab.js。
 
与此同时,我们之前在dist/index.html里的 <script type="text/javascript" src="ab.js"></script></body>不就可以起到作用了吗?让我们在磁盘里找到该文件打开,发现控制台输出了:
 

用图解描述上述过程,,webpack 递归地构建一个依赖树,这个依赖树包括你应用所需的每个模块,然后将所有模块打包为少量的包(bundle) - 通常只有一个包 - 可由浏览器加载。
 
 
2多入口文件
 
2-1上述例子中,我们只在entry中写入了一个入口文件,那我们能不能一次写入多个入口文件呢?这当然是可以的,首先修改我们在component的文件结构:
 

c.js/d.js/cd.js和a.js/b.js/ab.js结构上完全一致,只是输出的文本不同,这里不多赘述,然后修改我们的webpack.config.js
 
var path = require('path')
module.exports = {
entry:{
ab:'./component/ab.js',
cd:'./component/cd.js'
},
output:{
filename:'[name].js',
path:path.resolve(__dirname,'dist'),
},
}
 
这里的name是占位符[name]分别对应entry中写入的[ab]和[cd],这表示,在dist下生成的将不再是上文提到的bundle.js,而是ab.js和cd.js两个JS文件
 
2-2再修改一下我们的dist/index.html:

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<script type="text/javascript" src="./ab.js"></script></body>
<script type="text/javascript" src="./cd.js"></script></body>
</html>
2-3然后同样是进入终端,写入webpack再回车,回到目录,此时已经生成了dist/ab.js和dist/cd.js两个文件
 

2-4在浏览器中打开dist/index.html,输出:
 
 
 
用图解描述上述过程如下,由于引用关系建立的依赖书,a/b/ab和c/d/cd分别被打包为两个bundle并被引入dist/index.html
 

3为输出文件添加哈希值标记,避免相同文件重新加载
 
在前后两次在终端输入webpack打包时,即使component中的所有文件都没有变化,资源是要重新加载一遍的。同理,在生产中,每次需要在代码中更新内容时,服务器都必须重新部署,然后再由所有客户端重新下载。 这显然是低效的,因为通过网络获取资源可能会很慢。 那么我们怎么才能避免这个问题呢———给output中的bundle文件提供hash值标记:
 
每次构建输出文件时,如果代码发生变化,输出的文件将生成不同的hash值,这时将重新加载资源,但如果代码无变化,输出文件hash值也不变化,系统就会默认使用原来缓存的输出文件
 
3-1修改我们的webpack.config.js:
 
var path = require('path')

module.exports = {
entry:{
ab:'./component/ab.js',
cd:'./component/cd.js'
},
output:{
filename:'[name]-[hash].js',
path:path.resolve(__dirname,'dist'),
},
}
打包后dist目录:(上个例子中的dist/ab.js和dist/cd.js已删掉)
 

写入hash值带来的新问题——每次都要更改dist/index.html中JS的src
 
因为我们生成的hash是不断变化的,与此同时index.html必须不断更改<script>标签中的src的值
 
4解决hash值带来的新问题
 
4-1使用html-webpack-plugin插件,webpack.config.js的输出模块对象有一个plugins属性,它是一个数组,数组项是创建的plugin对象
在终端写入npm install html-webpack-plugin --save-dev,安装完毕后修改webpack.config.js的配置:
 
var path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry:{
ab:'./component/ab.js',
cd:'./component/cd.js'
},
output:{
filename:'[name]-[hash].js',
path:path.resolve(__dirname,'dist'),
},
plugins:[
new HtmlWebpackPlugin()
]
}
 
4-2在终端里输入webpack回车,打开我们的dist/index,居然已经自动写入了src带hash值的script标签!
 

【注意】这次的dist/index.html是webpack自动生成的,而以前的例子都是我们手动写入的
 
5为生成的index.html指定模版
 
5-1但让我们想一想另外一个问题,这个dist/html是自动生成的,我们能不能做一些改造,比如指定一个模版。用开发开发文件中的component/index.html为模版生成dist.html呢?先创建一个component/index.html文件,写入:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>这是开发文件中的模版HTML</title>
</head>
<body>
</html>
 
5-2修改我们的webpack.config.js:

var path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry:{
ab:'./component/ab.js',
cd:'./component/cd.js'
},
output:{
filename:'[name]-[hash].js',
path:path.resolve(__dirname,'dist'),
},
plugins:[
new HtmlWebpackPlugin({
template:'./component/index.html'
})
]
}
5-3在HtmlWebpackPlugin的参数对象中写入template属性,指定为component/index.js,回到终端,写入webpack,然后让我们看一看dist/index.html
 

用图解描述上述过程:
 
 
6用webpack打包多页面应用程序(MPA)
谈谈SPA(sing page application)与MPA(mutiple page aplication),SPA和MPA 指的是单页面应用程序和多页面应用程序,之前我们打包的都是SPA,那么怎么打包MPA呢。很简单,在plugins中写入多个HtmlWebpackPlugin对象便可,这时候需要指明不同文件的filename属性值,以及chunks属性值——它们对应的bundle文件
6-1改写一下我们的webpack.config.js文件:
 
 
var path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry:{
ab:'./component/ab.js',
cd:'./component/cd.js'
},
output:{
filename:'[name]-[hash].js',
path:path.resolve(__dirname,'dist'),
},
plugins:[
new HtmlWebpackPlugin({
filename:'ab.html',
template:'./component/index.html',
chunks:['ab']
}),
new HtmlWebpackPlugin({
filename:'cd.html',
template:'./component/index.html',
chunks:['cd']
})
]
}
6-2打包后在dist中生成了dist/ab.html和dist/cd.html,浏览器打开ab.html,控制台输出:
 
 
浏览器打开cd.html,控制台输出:
 
 
图解上述过程:
 

 【完】--喜欢这篇文章的话不妨关注一下我哟

【webpack学习笔记(一)】流行的前端模块化工具webpack初探的更多相关文章

  1. 【webpack】流行的前端模块化工具webpack初探

    从开发文件到生产文件   有一天我突然意识到一个问题,在使用react框架搭建应用时,我使用到了sass/less,JSX模版以及ES6的语法在编辑器下进行开发,使用这些写法是可以提高开发的效率.可是 ...

  2. 前端模块化工具-webpack

    详解前端模块化工具-webpack webpack是一个module bundler,抛开博大精深的汉字问题,我们暂且管他叫'模块管理工具'.随着js能做的事情越来越多,浏览器.服务器,js似乎无处不 ...

  3. 前端模块化工具--webpack学习心得

    话说前头 webpack前段时间有听说一下,现在已经到了3.x的版本,自己没去接触.因为之前使用gulp来作为自己的项目构建工具.现在感觉gulp使用的趋势在减少.现在这段时间去接触了webpack, ...

  4. 前端模块化工具--webpack使用感受

    话说前头 webpack前段时间有听说一下,现在已经到了3.x的版本,自己没去接触.因为之前使用gulp来作为自己的项目构建工具.现在感觉gulp使用的趋势在减少.现在这段时间去接触了webpack, ...

  5. 详解前端模块化工具-webpack

    webpack是一个module bundler,抛开博大精深的汉字问题,我们暂且管他叫'模块管理工具'.随着js能做的事情越来越多,浏览器.服务器,js似乎无处不在,这时,使日渐增多的js代码变得合 ...

  6. angular学习笔记(2)- 前端开发环境

    angular1学习笔记(2)- 前端开发环境 1.代码编辑工具 2.断点调试工具 3.版本管理工具 4.代码合并和混淆工具 5.依赖管理工具 6.单元测试工具 7.集成测试工具 常见的前端开发工具 ...

  7. 《从零开始学Swift》学习笔记(Day4)——用Playground工具编写Swift

    Swift 2.0学习笔记(Day4)——用Playground工具编写Swift 原创文章,欢迎转载.转载请注明:关东升的博客 用Playground编写Swift代码目的是为了学习.测试算法.验证 ...

  8. 【原】webpack学习笔记

    之前在react的项目中有用过webpack,不过没有认真的去研究,这段时间又重新好好的学习一下webpack,发觉 webpack是一个很强大的东西.而且很好用,方便,接下来主要是做一下学习的笔记 ...

  9. webpack学习笔记一:安装webpack、webpack-dev-server、内存加载js和html文件、loader处理非js文件

    一 .webpack学习环境准备: 1:window系统 2:安装node.js  官方网址 下载好后下一步下一步安装即可 安装步骤略过....... 3:nrm的安装 打开cmd命令控制台 输入:n ...

随机推荐

  1. 内网穿透+VS2015自带IIS express实现本地调试(微信等需要将开发环境暴漏到外网的情况使用)

    今天一个兼职结束了,又要开始寻找新的兼职公司了 ,为了贴补家用啊,为了给儿子更好的生活加油! 抒情完毕进入正题,本篇文章要解决的问题是其实在开发微信支付,微信公众号等回调地址必须是外网可访问的80端口 ...

  2. node将excel内容转json

    小颖分享的这个方法,前提是你已经安装了node,如果大家不知道自己是否安装过node可以打开cmd,然后执行:node -v,如果安装过,你会看到你安装的node版本号,如果没有安装请先安装node. ...

  3. BZOJ 4085:[Sdoi2015]bigyration(SDOI 2015 round 2 Day 1)

    别人家的神选系列.Day2根本不能做QAQ 题目描述:给定两个字符串集合,一个长度为n,另一个为m,求有多少个数字对i,j,满足xi+yj能由一个(n+m)/2的字符串旋转拼接而成 我们枚举长度较长的 ...

  4. 令人费解的java泛型

         对于我们java中的泛型,可能很多人知道怎么使用并且使用的还不错,但是我认为想要恰到好处的使用泛型,还是需要深入的了解一下它的各种概念和内部原理.本文将尽可能的囊括java泛型中的重要的概念 ...

  5. Ancient Cipher UVa1339

    这题就真的想刘汝佳说的那样,真的需要想象力,一开始还不明白一一映射是什么意思,到底是有顺序的映射?还是没顺序的映射? 答案是没顺序的映射,只要与26个字母一一映射就行 下面给出代码 //Uva1339 ...

  6. iOS异步处理

    有过编程经验的人,基本都会接触到多线程这块. 在java中以及Android开发中,大量的后台运行,异步消息队列,基本都是运用了多线程来实现. 同样在,在ios移动开发和Android基本是很类似的一 ...

  7. Keepalived安装与配置

      下载并解压Keepalived安装包到两台nginx所在的服务器   192.168.200.1   192.168.200.2     执行编译安装(安装目录设置为 /usr/local/kee ...

  8. 每天一个linux命令(29)--Linux chmod命令

    chmod 命令用于改变Linux 系统文件或目录的访问权限.用它控制文件或目录的访问权限.该命令有两种用法.一种是包含字母和操作符表达式的文字设定法:另一种是包含数字的数字设定法. Linux系统中 ...

  9. 【2017-03-05】函数基础、函数四种结构、ref和out参数、递归

    一.函数基础 1.函数/方法:非常抽象独立完成某项功能的一个个体 2.函数的作用: 提高代码的重用性提高功能开发的效率提高程序代码的可维护性 3.分类 固定功能函数高度抽象函数 4.函数四要素:输入, ...

  10. 学习HTML5一周的收获4

    /* [CSS常用文本属性]  * 1.字体.字号: font-weight:字体的粗细,可选属性值:bold加粗  lighter细体  100~900数值(400正常,700 bold)   fo ...