1.什么是工程化开发
软件工程的工程化开发概念由来已久,但对于前端开发来说,我们没有像VS或者eclipse这样量身打造的IDE,因为在大多数人眼中,前端代码无需编译,因此只要一个浏览器来运行调试就行了。但是时至今日,互联网特别是移动互联网为前端开发带来了更大的机会,同时前端代码也变得越来越复杂,越来越难以管理,因此前端工程化开发的工作可以说是刻不容缓。
那么前端工程化开发到底需要解决哪些前端工程师们火烧眉毛的问题呢?个人认为至少包含以下几点:
1. 专业的IDE支持,完成包括项目初始化,语法提示,项目编译,打包等工作。
2. 良好的模块化代码管理结构,模块化可以使我们编写的组件或者代码达到高度复用,降低代码间的耦合性;同时可以良好的与第三方组件兼容。
3. 简单易配置的前端测试环境,完成组件的单元测试,页面的集成测试;同时提供良好的DEBUG环境,可以很好的定位错误的所在以及错误的详细信息。
4. 静态资源(图片/字体/CSS/JS等)的良好管理方案,一是静态的文件版本问题,二是对于小图标自动转BASE64,减少HTTP请求
5. 完整的代码版本管理,打包,发布,多环境部署,测试反馈等运维支持
当然,以上只是个人意见。每个公司、每个项目的情况不一样,所需要的条件都会有所不同。
以上这些需求,在以前基本上都是不敢想象的,这些工具都需要访问文件系统或者网络,很少有比较完整的解决方案。(好了,早期也有类似于Ant这样的Java解决方法,对前端开发人员要求过高而且不是很好用)好在,伟大的NodeJs诞生了,于是乎,基于NodeJs出现了很多优秀框架,像Grunt和 Gulp等。
好了,今天的主题是webpack!让我们来看下webpack作为后起之秀,是如何对前端工程化进行支持的!
2. 动态生成HTML
大家注意,这里所说的动态生成HTML,是指我们使用webpack来动态产生我们最终所期望的HTML文件,而不是指在浏览器运行时使用JS生成HTML片段。
那为什么要动态生成HTML,我自己写不行吗?答案当然是可以的。
之所以要动态生成,主要是希望webpack在完成前端资源打包以后,自动将打包后的资源路径和版本号写入HTML中,达到自动化的效果。
大家可以回想一下我们之前的三篇文章中介绍的案例,在那个练手的项目中,我们页面上的script标签是我们自己写的,那么如果我们需要给JS添加上版本号的话,岂不是每次都的去修改?还有CSS,都是内嵌在JS中的,待JS加载后再创建style标签,然后写入css内容。这么做的话,浏览器需要先等待JS加载完成后,才能生成CSS样式,页面上会有一个等待过程,这个过程页面是完全没有样式的。这当然不是我们所想要的。
我们的目标是:webpack根据指定的模板,插入打包编译后CSS文件路径;插入打包生成的JS的文件路径。并且还需要为二者添加版本号。另外,我们还可以同时将HTML进行压缩,进一步减少文件大小。
3.初始化项目、安装依赖
我们这里默认大家的开发环境已经安装了npm以及webpack,尚未安装的同学可以自行安装。
A.新建一个空项目,然后通过 npm init 对项目进行初始化,按照提示输入项目的基本信息,然后生成package.json文件。这个文件里面会保存我们即将安装的一些npm插件信息,便于二次移植开发。
B.安装项目所需依赖:
npm install css-loader jquery@1 style-loader html-webpack-plugin --save-dev
npm install extract-text-webpack-plugin --save-dev
依次为css加载器,jquery,style加载器,HTML生成插件以及文件提取插件。
最终package.json的依赖声明得到更新:
"devDependencies": { "css-loader": "^0.23.0", "extract-text-webpack-plugin": "^0.9.1", "html-webpack-plugin": "^1.7.0", "jquery": "^1.11.3", "style-loader": "^0.13.0", "webpack": "^1.12.9", "webpack-dev-server": "^1.14.0" }
4. 创建目录,添加测试文件
准备就绪,我们开始创建项目目录:
- webapp - src #代码开发目录 - css #css目录,按照页面(模块)、通用、第三方三个级别进行组织 + page + common + lib + img #图片资源 - js #JS脚本,按照page、components进行组织 + page + view #HTML模板 - dist #webpack编译打包输出目录,同样按照css/js/img进行组织 + css + js + view + node_modules #所使用的nodejs模块 package.json #项目配置 webpack.config.js #webpack配置 README.md #项目说明
dist目录也可以不创建任何子目录,这里为了方便查看,将js/css/html分开存放。
然后我们在src目录创建几个测试文件,详细的代码大家可以前往https://github.com/xiaoyunchen/webpack/tree/master/src 查看源码。
我们先来看下index.js的内容:
1 //引入CSS 2 require("../../css/lib/reset.css"); 3 require("../../css/common/global.css"); 4 require("../../css/page/index.css"); 5 6 document.write('Hello Index Js');
代码很简单,主要是引入了几个css文件。再看下我们的 /view/index.html 这个模板的内容
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Index主页</title> <meta name="author" content="https://github.com/xiaoyunchen/webpack"/> <meta name="date" content="2015-12-3"/> <meta name="description" content="基于webpack的前端工程化开发解决方案探索"/> <!-- 作者:https://github.com/xiaoyunchen/webpack 时间:2015-12-03 描述:head中无需再引入css以及facicon,webpack将根据入口JS文件的要求自动实现按需加载或者生成style标签 --> </head> <body> <p>Hello,Webpack!!</p> <!-- 作者:chyun532@qq.com 时间:https://github.com/xiaoyunchen/webpack 描述:body中同样无需单独引入JS文件,webpack会根据入口JS文件自动实现按需加载或者生成script标签,还可以生成对应的hash值 --> </body> </html>
这是一个简单的HTML模板,值得一提的是我们在这里并没有引入任何的CSS和JS,我们希望通过webpack打包来自动生成。(这里的模板还支持 Blueimp)
5. webpack配置
最后是我们的重头戏,webpack.config.js,我们将在这里配置一些webpack任务,来完成我们的需求:
1 var path=require('path'); 2 var webpack = require('webpack'); 3 var ExtractTextPlugin = require("extract-text-webpack-plugin"); 4 var HtmlWebpackPlugin = require('html-webpack-plugin'); 5 module.exports={ 6 entry:{ 7 index:"./src/js/page/index.js", 8 }, 9 output:{ 10 path: path.join(__dirname,'dist'), 11 publicPath: "/webpack/dist/", 12 filename: "js/[name].js", 13 chunkFilename: "js/[id].chunk.js" 14 }, 15 module: { 16 loaders: [ //加载器 17 {test: /.css$/, loader:ExtractTextPlugin.extract("style", "css") } 18 ] 19 }, 20 plugins:[ 21 new ExtractTextPlugin("css/[name].css"), //单独使用style标签加载css并设置其路径 22 new HtmlWebpackPlugin({ //根据模板插入css/js等生成最终HTML 23 favicon:'./src/img/favicon.ico', //favicon路径 24 filename:'/view/index.html', //生成的html存放路径,相对于 path 25 template:'./src/view/index.html', //html模板路径 26 inject:true, //允许插件修改哪些内容,包括head与body 27 hash:true, //为静态资源生成hash值 28 minify:{ //压缩HTML文件 29 removeComments:true, //移除HTML中的注释 30 collapseWhitespace:false //删除空白符与换行符 31 } 32 }) 33 ] 34 };
这个配置文件再之前的文章中我们已经讲过很多次了,这里就不在赘述,只对其中几点比较关键的点进行分析:
17行:css加载器,只是这里改用了文件提取插件,将css提取出来单独作为一个文件进行存储。
21行:配置提取出来的css文件名以及存放路径
22行: html-webpack-plugin 这是webpack中生成HTML的插件,里面有详细的配置说明,大家可以前往查看。
23行:配置favicon,通过webpack引入同时可以生成hash值
24行:配置最终生成HTML文件存放路径
25行:我们所使用的模板
26行:允许webpack修改哪些内容,可选值为head和body,true的话是都可以修改
27行:为静态资源生成hash值
28行:压缩最终生成的HTML文件,相关配置参数请前往 html-minifer 查看。(这里为了方便后面查看,没有移除HTML中的空白符与换行符。)
OK,下面我们在项目的根目录下运行 webpack 打包命令完成项目打包:
打包成功后,我们前往/dist/view目录下查看生成的index.html是什么样
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Index主页</title> <meta name="author" content="https://github.com/xiaoyunchen/webpack"> <meta name="date" content="2015-12-3"> <meta name="description" content="基于webpack的前端工程化开发解决方案探索"> <link rel="shortcut icon" href="/webpack/dist/favicon.ico?69fed78822d5f8d3895c"><link href="/webpack/dist/css/index.css?69fed78822d5f8d3895c" rel="stylesheet"></head> <body> <p>Hello,Webpack!!</p> <script src="/webpack/dist/js/index.js?69fed78822d5f8d3895c"></script></body> </html>
可以看到生成的文件除了保留原模板中的内容以外,还根据入口文件index.js的定义,自动添加需要引入CSS与JS文件,以及favicon,同时还添加了相应的hash值。
运行这个文件,可以看到代码正常,引入的文件路径也都OK!
是的,我们最初想要通过动态生成HTML的目的达到了。
webpack根据指定的模板,插入打包编译后CSS文件路径;
插入打包生成的JS的文件路径。并且还需要为二者添加版本号。
另外,我们还可以同时将HTML进行压缩,进一步减少文件大小。
今天的分享就到这里。可能大家还有些疑问:按需加载的JS/CSS也会被提取出来吗?下一章我们将继续探索这个问题。