Webpack在APS.NET mvc中使用
前戏乱侃:
话说由于某js引擎得到推广,使js得以脱离浏览器,可在本地很好执行,于是乎NodeJS出现了。js可以开发本地应用了,开发这种应用那得有很多js文件吧,总要分分模块才好管理吧,模块分了之后,模块之间怎么引入呢?java中可以用import,C#中可以用using。嗯,js应用中就用require("path/to/module")吧,总应该为之定个规范吧,于是这种js引入方式就被定义为了CommonJS。后来某人受到启发,既然本地应用中可以这样子引用,web App中可否也使用这样的规范。于是require("path/to/module",function(){})这种web js引入方式得以应用,名曰AMD规范,大概的意思就是,模块加载好后,执行后面的方法。以此同时也出现了另一个模块加载规范CMD。其实AMD和CMD规范都差不多,都是用于web的模块加载器,AMD使用的加载器是RequireJS,CMD使用的加载器是SeaJS,RequireJS老外用得比较多,SeaJS国人用得比较多。再后来出现Grunt,Gulp,Webpack的打包工具。NodeJS是什么,其实他就相当于能让js在命令行执行的工具。以上乱侃over,懂得不多,不对之处,请指正。
正文开始
当我还在兴奋的追随SeaJS时,甚至都还没开始搞清楚时,已经开始有人宣判了SeaJS的死刑。在使用SeaJS开发一段时间后,是的,我赞同了这个观点,特别是在模块的打包方面,我至今还是没有找到一个好的解决方案(可能是我知识不够)。感觉SeaJS只做到了模块的分解,却没有找到处理模块合并很好的解决方案。说白了SeaJS只是一个加载器,并不是一个打包工具,它缺的就是一个打包工具。直到后来看到了webpack,在初步了解了一下他的功能后,我真的欣喜若狂,感觉这就是我苦苦寻求的前端构建解决方案。
想一下开发单页面应用程序时,我们的需求:
- 1、我们希望每个view只加载与之相关的资源,,其他view用到的资源在点击首次进入页面时再加载,从而减少页面资源的请求,提高页面加载速度;
- 2、引入其他插件时,我们希望在首次引入插件时,同时把插件用到css引入到页面中,比如 art dialog 插件,重复引用,不会重新加载;
- 3、css,image,js编译、压缩打包、生成版本号,自动替换,并加入到html或schtml中。
是的,webpack都能满足了以上的需求。除了以上的功能外,它还支持各种模块的引入方式天然的支持CommonJS、AMD、CMD,但EA6需要引入相关的loader。
也就是说在一个模块之中,你可以使用以下的定义方式:
说了这么多,上一个我在用的配置方案。自动化构建,说白了玩的就是配置呀,配置好了就是一劳永逸了。
我的方案是:Asp.net mvc+backbone+bootstrap
Asp.net mvc...不解释了;
backbone:单页面应用路由,前端mvc等;
bootstrap 前端css框架。
配置文件的大概思路:
首先我希望登录和主应用的入口是分开的,因此设置了两个程序入口;但这两个入口都同时需要jQuery,因此我将jQuery单独提取出来;
其次,我给mvc View设置了一个模板:views/home/index_tpl.cshtml。每次修改view时只需要修改模板就可以了,构建完后会生成目标页面views/home/index.cshtml,此时index.cshtml中会自动加入各种入口文件
//index_tpl.cshtml @{ ViewBag.Title = "后台管理-首页"; } <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> <div class="aliyun-console-topbar"> </div> <div class="container-body sidebar-full"> <div class="sidebar"></div> <div class="main" id="main"> </div> </div> </body> </html>
//构建完后的index.cshtml,webpack会插入各种入口文件 @{ ViewBag.Title = "后台管理-首页"; } <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>后台管理 . NOCOQ</title> <link href="/administration/dist/styles/vendors.90866b.css" rel="stylesheet"><link href="/administration/dist/styles/index.2e18b3.css" rel="stylesheet"></head> <body> <div class="aliyun-console-topbar"> </div> <div class="container-body sidebar-full"> <div class="sidebar"></div> <div class="main" id="main"> </div> </div> <script type="text/javascript" src="/administration/dist/vendors.7cf649.js"></script><script type="text/javascript" src="/administration/dist/index.7cf649.js"></script></body> </html>
最后,说一个小插曲,使用webpack生成目标view后,运行起来后发现中文居然是乱码,找了好久才发现原来是因为webpack生成的文件编码是utf-8 无 bom(utf-8 without bom)格式,而mvc的.cshtml的格式是utf-8 bom格式。于是找到了webpack-utf8-bom这个plugin才顺利的解决了问题。
其他的信息,备注中都有了。
使用webpack资源的引入、加载和打包,一切都这么自然,我还能不喜欢它吗?
//package.json 好像有点不全,不全就按webpack.config.js中找吧。
{ "name": "src", "version": "1.0.0", "description": "000", "main": "webpack.config.js", "dependencies": { "babel-core": "^6.17.0", "babel-loader": "^6.2.5", "babel-preset-es2015": "^6.16.0", "bootstrap": "^3.3.7", "css-loader": "^0.25.0", "expose-loader": "^0.7.1", "html-webpack-plugin": "^2.22.0", "jquery": "^3.1.1", "jquery-ui": "^1.12.1", "loader-utils": "^0.2.16", "lodash": "^4.16.4", "style-loader": "^0.13.1", "wangeditor": "^2.1.22", "webpack": "^1.13.2" }, "devDependencies": { "art-template": "^3.0.3", "art-template-loader": "^0.1.4", "backbone": "^1.3.3", "clean-webpack-plugin": "^0.1.13", "copy-webpack-plugin": "^3.0.1", "extract-text-webpack-plugin": "^1.0.1", "file-loader": "^0.9.0", "html-webpack-plugin": "^2.22.0", "mime": "^1.3.4", "underscore": "^1.8.3", "url-loader": "^0.5.7" }, "scripts": { "test": "" }, "author": "", "license": "ISC" }
//webpack.config.js
var path = require(‘path‘); var webpack = require(‘webpack‘); var HtmlWebpackPlugin = require(‘html-webpack-plugin‘); // 自动写入将引用写入html var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin"); // 提取公共模块 var ExtractTextPlugin = require("extract-text-webpack-plugin");// 提取/分离css var CleanWebpackPlugin = require(‘clean-webpack-plugin‘); // 删除文件 var CopyWebpackPlugin = require(‘copy-webpack-plugin‘); // 拷贝文件 var BomPlugin = require(‘webpack-utf8-bom‘);//将文件转成utf-8 bom格式,解决中文乱码的问题 //console.log(require.resolve("bootstrap")); //定义了一些文件夹的路径 var ROOT_PATH = path.resolve(__dirname); var CTRL_ROOT_PATH = path.resolve(__dirname, "administration"); var CTRL_SRC_PATH = path.resolve(CTRL_ROOT_PATH, ‘dev‘); var CTRL_DIST_PATH = path.resolve(CTRL_ROOT_PATH, ‘dist‘); //console.log(CTRL_ROOT_PATH) module.exports = { entry: { index: path.resolve(CTRL_SRC_PATH, ‘index.js‘), login_index: path.resolve(CTRL_SRC_PATH, ‘login_index.js‘), vendors: [‘jquery‘, ‘datepicker‘], //"jquery-ui": ["jquery-ui/themes/base/core.css", "jquery-ui/themes/base/datepicker.css", "jquery-ui/themes/base/theme.css"], //"style": [path.resolve(CTRL_SRC_PATH, ‘styles/style.css‘)], }, output: { //context: path.resolve(__dirname, ‘scripts‘), path: path.resolve(CTRL_DIST_PATH), publicPath: ‘/administration/dist/‘,//当生成的资源文件和站点不在同一地方时需要配置改地址 e.g.:站点在src,资源生成到/src/static/dist,那么publicPath="/static/dist" filename: "[name].[hash:6].js", chunkFilename: "[id].chunk.js", }, plugins: [ //new webpack.ProvidePlugin({$: ‘jquery‘}), new ExtractTextPlugin("styles/[name].[contenthash:6].css", { allChunks: false }), new HtmlWebpackPlugin({ title: ‘后台管理‘, template: path.resolve(CTRL_ROOT_PATH, ‘views/home/index_tpl.cshtml‘), filename: path.resolve(CTRL_ROOT_PATH, ‘views/home/index.cshtml‘), chunks: [‘index‘, ‘vendors‘], // 配置要添加的模块 inject: ‘body‘ }), new HtmlWebpackPlugin({ title: ‘登录后台管理‘, template: path.resolve(CTRL_ROOT_PATH, ‘views/account/login_tpl.cshtml‘), filename: path.resolve(CTRL_ROOT_PATH, ‘views/account/login.cshtml‘), chunks: [‘login_index‘, ‘vendors‘], // 配置要添加的模块 inject: ‘body‘ }), new CommonsChunkPlugin(‘vendors‘, ‘vendors.[hash:6].js‘), new CleanWebpackPlugin([‘dist‘, ‘build‘], { root: CTRL_ROOT_PATH, verbose: true, dry: false, //exclude: ["dist/1.chunk.js"] }), new BomPlugin(true, /\.(cshtml)$/),//解决cshtml中文乱码的问题 ], module: { /***解决动态路径警告的问题***/ // require unknownContextRegExp: /$^/, unknownContextCritical: false, // require(expr) exprContextRegExp: /$^/, exprContextCritical: false, // require("prefix" + expr + "surfix") wrappedContextRegExp: /$^/, wrappedContextCritical: false, /***end****/ loaders: [ { test: require.resolve(‘jquery‘), loader: ‘expose?$!expose?jQuery‘ }, // 将jQuery暴露到全局变量中 { test: /\.css$/, loader: ExtractTextPlugin.extract(‘style-loader‘, ‘css-loader?‘ + JSON.stringify({ discardComments: { removeAll: true } })) }, { test: /\.(png|jpg|gif|woff|woff2|eot|ttf|svg)(\?[a-z0-9]+)?$/, loader: ‘url-loader?limit=1000&name=fonts/[name].[hash:6].[ext]‘ }, // 处理图片url //{ test: /\.(png|jpg|gif)(\?[a-z0-9]+)?$/, loader: ‘url-loader?limit=1000&name=images/[name].[hash:8].[ext]‘ }, // 处理图片url limit=1000 小于1kb则生成base64 //{ test: /\.css$/, loader: "style!css" }, { test: /\.js$/, exclude: /(node_modules|bower_components)/, loader: ‘babel‘, // ‘babel-loader‘ is also a valid name to reference query: { presets: [‘es2015‘] } }, { test: /\.tpl/, loader: ‘art-template-loader‘ }, ] }, resolve: { alias: { "datepicker": "jquery-ui/ui/widgets/datepicker", } } };
参考资料:
Webpack https://webpack.github.io/docs/
NodeJS https://nodejs.org/
Grunt http://gruntjs.com/
Gulp http://gulpjs.org/
SeaJS http://seajs.org/docs/
RequireJS http://requirejs.org/