用webpack来构建纯前端项目已经司空见惯了,可是,其实并无不少公司真正的实现先后端分离,因此使用asp.net mvc开发仍是很广泛的。
以前使用.net mvc + vue + elementui 开发感受驾轻就熟,效率高了不少,可是遇到一个问题:在使用ES6语法时,会遇到兼容性问题。
刚开始的时候采用了traceur-compiler
来进行实时编译,后来以为这个总不是best practise,因而开始尝试使用自动构建的方式来实现。javascript
最初的打算是使用Gulp来进行自动构建,可是发现使用Gulp有如下几个问题:css
综合考虑下仍是决定直接使用webpack来作:html
-w
就能够实现"livereload"引入webpack的最基本目的是为了编译ES6的代码,所以只须要引入babel
就能够了。前端
npm init
来初始化package.json
文件(一路回车便可)npm install --save-dev webpack babel-core babel-loader babel-preset-2015
,这样最基础的插件就安装好了clean-webpack-plugin
,用于清除目录,第二个是assets-webpack-plugin
,这个用以生成将js文件和带chunkhash值的文件映射关系的文件,最后还有一个用以装逼的插件webpack-notifier
,这个是用来通知webpack执行状态的 (注意:这里若是须要使用babel-polyfill
也能够引入)这时没什么好多说的,都是webpack的基本使用,设置entry,module里面设置使用babel-loader
来加载".js"文件
同时在执行webpack脚本时增长'-w'参数,就能够实现修改js文件后自动编译了vue
为了实现长效缓存,戳这里,还须要使用'[name].[chunkhash].js'
来给编译后的js名添加hash值,可是这样就产生了不少新的问题java
assets-webpack-plugin
插件,能够在构建时,生成filename与filename-chunkhash的映射关系。并将这个映射关系保存下来,这样就能够读取这个文件,以相似反射的方式加载考虑到若是须要每次新增一个js文件,都要去修改webpack.config.js的入口(entry
)未免有点麻烦,因而考虑也用相似反射的思路,来读取用来存放业务js代码的文件夹,把每一个".js"都做为入口文件的一个对象:node
var readFile = function (path) { var fs = require('fs'); var files = fs.readdirSync('./' + path); var jsFiles = files.filter((f) => { return f.endsWith('.js'); }); var ret = {}; jsFiles.forEach(function (item) { var temp = item.substring(item, item.indexOf(".js")); ret[temp] = "./" + SOURCE_DIRECTORY + "/" + item; }); return ret; };
最终生成出来的样子以下所示:webpack
// entry: { // wp:[ './app/webpack.js'] // 独立的业务文件 // }, // 入口文件定义
最终的webpack.config.js
文件应该长这样:git
'use strict'; const AssetsPlugin = require('assets-webpack-plugin'); // 将每一个资源文件的原生filename和chunkhashname一一对应起来的插件 const CleanPlugin = require('clean-webpack-plugin'); // 清空目录的插件 const path = require('path'); // 顾名思义,路径相关操做的插件 const webpack = require('webpack'); // 主角,webpack const pkg = require('./package'); // package.json var WebpackNotifierPlugin = require('webpack-notifier'); const SCRIPTS_ROOT = 'Content/Scripts/'; const SOURCE_DIRECTORY = 'app'; // 业务文件名 const BUILD_DIRECTORY = 'build'; // 构建目录名 const BUILD_DROP_PATH = path.join(__dirname, SCRIPTS_ROOT, BUILD_DIRECTORY); //const BUILD_DROP_PATH = path.resolve(__dirname, BUILD_DIRECTORY); // 构建全路径 const WEB_ROOT = path.join(__dirname, SCRIPTS_ROOT); // 当前上下文 const CHUNK_FILE_NAME = '[name].[chunkhash].js'; var readFile = function (path) { var fs = require('fs'); var files = fs.readdirSync('./' + path); var jsFiles = files.filter((f) => { return f.endsWith('.js'); }); var ret = {}; jsFiles.forEach(function (item) { var temp = item.substring(item, item.indexOf(".js")); ret[temp] = "./" + SOURCE_DIRECTORY + "/" + item; }); return ret; }; var webpackEntry = readFile(path.join(SCRIPTS_ROOT, SOURCE_DIRECTORY)); console.log(webpackEntry); const config = { context: WEB_ROOT, // 声明上下文 // entry: { // wp:[ './app/webpack.js'] // 独立的业务文件 // }, // 入口文件定义 entry: webpackEntry, module: { loaders: [ { test: /\.js$/, loader: 'babel-loader', query: { presets: ['es2015'] }, exclude: /node_modules/ }, { test: /(\.css$)/, loaders: ['style-loader', 'css-loader', 'postcss-loader'] }, { test: /\.(png|woff|woff2|eot|ttf|svg)$/, loader: 'url-loader?limit=100000' } ] }, // 使用babel编译js output: { path: BUILD_DROP_PATH, filename: CHUNK_FILE_NAME, chunkFilename: CHUNK_FILE_NAME }, // 输出 plugins: [ new AssetsPlugin({ filename: 'webpack.assets.json', path: BUILD_DROP_PATH, prettyPrint: true }), // 将filename 和 filename.chunkhash对应起来 new CleanPlugin(BUILD_DROP_PATH), // 构建前先清空文件夹 new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false }, output: { comments: false } }), // 压缩混淆 new WebpackNotifierPlugin(), // 通知结果 ], }; module.exports = config;
到目前为止,webpack能作的事就结束了,接下来.net怎么配合呢?github
前面提到了,编译出来的js文件名是含有chunkhash值的,因而最理想的使用方法就是在开发过程当中感受不到变化,仍是像以前同样,只须要写"filename"就能够引入含有"chunkhash值"的编译后的js文件。
作法以下:
首先,扩展一下UrlHelper:
public static class WebpackExtension { public static string GetResourceUrlHashChunk(this UrlHelper helper, string fileName) { JObject webpackAssetsJson = null; var applicationBasePath = System.AppDomain.CurrentDomain.BaseDirectory; string packageJsonFilePath = $"{applicationBasePath}\\{"package.json"}"; using (StreamReader packageJsonFile = File.OpenText(packageJsonFilePath)) { using (JsonTextReader packageJsonReader = new JsonTextReader(packageJsonFile)) { JObject packageJson = (JObject)JToken.ReadFrom(packageJsonReader); JObject webpackConfigJson = (JObject)packageJson["customConfig"]["webpackConfig"]; string webpackAssetsFileName = webpackConfigJson["assetsFileName"].Value<string>(); string webpackBuildDirectory = webpackConfigJson["buildDirectory"].Value<string>(); string webpackAssetsFilePath = $"{applicationBasePath}\\{webpackBuildDirectory}\\{webpackAssetsFileName}"; using (StreamReader webpackAssetsFile = File.OpenText(webpackAssetsFilePath)) { using (JsonTextReader webpackAssetsReader = new JsonTextReader(webpackAssetsFile)) { webpackAssetsJson = (JObject)JToken.ReadFrom(webpackAssetsReader); } } return "~/" + webpackBuildDirectory + webpackAssetsJson.SelectToken(fileName).Value<string>("js"); } } } }
这段代码作的事情就是把AssetsPlugin
这个插件生成的包含映射关系的文件读出来,而后根据文件名找到真实路径
因而在前台(cshtml)页面中使用时,能够直接使用如下方法:
<script type="text/javascript" src="@Url.Content(@Url.GetResourceUrlHashChunk("webpack"))"></script>
注:webpack是我这个js的名称
事情到这里尚未结束,在发布时又发生了情况,那就是这些编译后的文件因为是动态生成的,没法包入项目文件,所以也就不会复制的发布目录中,解决方法以下:
在项目根目录下的Properties\PublishProfiles
文件夹中,会保存发布的配置文件,因为visual studio是使用MSBuild进行编译发布的,所以在对MSbuild略加研究以后,pubxml
的内容以下:
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <WebPublishMethod>FileSystem</WebPublishMethod> <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration> <LastUsedPlatform>Any CPU</LastUsedPlatform> <SiteUrlToLaunchAfterPublish /> <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish> <ExcludeApp_Data>False</ExcludeApp_Data> <publishUrl>F:\WebDemo\webpack</publishUrl> <jsPublishUrl>F:\WebDemo\webpack\Content\Scripts\build</jsPublishUrl> <!--这里务必设为false--> <DeleteExistingFiles>False</DeleteExistingFiles> <PrecompileBeforePublish>True</PrecompileBeforePublish> <EnableUpdateable>True</EnableUpdateable> <DebugSymbols>False</DebugSymbols> <WDPMergeOption>DonotMerge</WDPMergeOption> </PropertyGroup> <ItemGroup> <!--定义须要copy到发布目录的文件 定义发布前须要先删除的文件夹--> <WebFiles Include="$(ProjectDir)\Content\Scripts\build\*.*" /> <OutputFiles Include="$(publishUrl)\**\*.*" /> </ItemGroup> <!--先删除以前老的发布文件--> <Target Name="RemoveDirectories" AfterTargets="AfterBuild"> <Delete Files="@(OutputFiles)" /> </Target> <!--将带hashchunk的压缩过的js文件copy到发布目录--> <Target Name="moveChunkhashJs" AfterTargets="RemoveDirectories"> <MakeDir Directories="$(jsPublishUrl)" Condition="!Exists('$(jsPublishUrl)')" /> <Copy SourceFiles="@(WebFiles)" DestinationFolder="$(jsPublishUrl)"> </Copy> </Target> </Project>
在vs2015里还能够直接安装webpack task runner 插件来使用
如图所示,还能够将webpack脚本和生成事件绑定起来了。
最后还有一个小坑:戳这里
若是你在命令行运行脚本没问题,可是使用webpack task runner 出了问题,颇有可能就是这个缘由,配置"外部Web公交"时,的位置顺序不对。
# 小结
其实还有不少能够优化的地方,好比第三方库,css等,可是目前已经能够知足初步需求了。接下来再作进一步的优化。
# 栗子
栗子