用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等,可是目前已經能夠知足初步需求了。接下來再作進一步的優化。
# 栗子
栗子