若是你如今正在使用Vue.js,當你構建一個原型的時候,你所須要作的一般就是經過<script>把Vue.js引入進來,而後就完事了。可是真實狀況每每不是這樣的。當咱們真正開發一個應用的時候,咱們不可避免的會用到一大堆的工具,模塊化、預處理器、熱模塊加載、代碼校驗和測試。這些工具對於一個須要長期維護的大型應用是必須的,可是項目初始化將會是讓人痛苦的事情。這就是爲何咱們作了vue-cli,讓一個簡單的命令行工具來幫助你快速的構建一個擁有強大構建能力的Vue.js項目。css
# 安裝vue-cli,npm全局安裝 npm install -g vue-cli
# 使用vue-cli初始化項目 vue init webpack my-project-vue
這個命令會從https://github.com/vuejs-temp...獲取webpack的模板,而且放到my-project-vue下
詳細參考:[](https://github.com/vuejs/vue-...html
執行過程以下:vue
vue init webpack my-project-vue This will install Vue 2.x version of the template. For Vue 1.x use: vue init webpack#1.0 my-project-vue # 這些都是提示可選的,按須要選擇便可,都是一些頗有名的js工具,由於測試學習,我全選了 ? Project name my-project-vue ? Project description A Vue.js project ? Author yuanyuanyuan ? Vue build standalone ? Install vue-router? Yes ? Use ESLint to lint your code? Yes ? Pick an ESLint preset Standard ? Setup unit tests with Karma + Mocha? Yes ? Setup e2e tests with Nightwatch? Yes vue-cli · Generated "my-project-vue". To get started: cd my-project-vue npm install npm run dev Documentation can be found at https://vuejs-templates.github.io/webpack
vuejs-templates/webpack下載和配置好以後,就須要使用npm將相關的依賴和模塊進行下載安裝node
# 進入到目錄 cd my-project-vue ls README.md config package.json static build index.html src test
由於vue 初始化的時候也把package.json生成了,npm 能夠經過這個json進行模塊和依賴安裝webpack
# 安裝依賴 npm install //安裝package.json裏的全部模塊依賴
以爲慢,能夠修改淘寶源:
npm config set registry https://registry.npm.taobao.org
git
# 開始運行 npm run dev DONE Compiled successfully in 6456ms > Listening at http://localhost:8080
可能會遇到缺乏包致使打開頁面出現404錯誤,能夠根據包提示安裝包
sudo npm install --save strip-ansi ansi-html html-entities
-save和save-dev能夠直接將包添加到package.json文件中github
至此完成安裝並運行,而且能夠在瀏覽器查看到效果:web
Welcome to Your Vue.js App
本文檔主要使用vue-webpack-boilerplate模板來理解,根據官方解釋:vue-router
webpack - A full-featured Webpack + vue-loader setup with hot reload, linting, testing & css extraction. 全功能的webpack + vue-loader模板,而且配置好熱更新等功能
npm run dev 這個命令會執行一個dev服務器本地監聽,能夠熱更新vuex
npm run dev: first-in-class development experience. * Webpack + vue-loader for single file Vue components. * State preserving hot-reload * State preserving compilation error overlay * Lint-on-save with ESLint * Source maps
npm run build 會建立生產環境的配置,會生成合並的文件
npm run build: Production ready build. * JavaScript minified with UglifyJS. * HTML minified with html-minifier. * CSS across all components extracted into a single file and minified with cssnano. * All static assets compiled with version hashes for efficient long-term caching, and a production index.html is auto-generated with proper URLs to these generated assets. * Use npm run build --reportto build with bundle size analytics.
vue-webpack-boilerplate模板的目錄架構大體以下:
. |-- build // 項目構建(webpack)相關代碼 | |-- build.js // 生產環境構建代碼 | |-- check-version.js // 檢查node、npm等版本 | |-- dev-client.js // 熱重載相關 | |-- dev-server.js // 構建本地服務器 | |-- utils.js // 構建工具相關 | |-- webpack.base.conf.js // webpack基礎配置 | |-- webpack.dev.conf.js // webpack開發環境配置 | |-- webpack.prod.conf.js // webpack生產環境配置 |-- config // 項目開發環境配置 | |-- dev.env.js // 開發環境變量 | |-- index.js // 項目一些配置變量 | |-- prod.env.js // 生產環境變量 | |-- test.env.js // 測試環境變量 |-- src // 源碼目錄 | |-- components // vue公共組件 | |-- store // vuex的狀態管理 | |-- App.vue // 頁面入口文件 | |-- main.js // 程序入口文件,加載各類公共組件 |-- static // 靜態文件,好比一些圖片,json數據等 | |-- data |-- .babelrc // ES6語法編譯配置 |-- .editorconfig // 定義代碼格式 |-- .gitignore // git上傳須要忽略的文件格式 |-- README.md // 項目說明 |-- favicon.ico |-- index.html // 入口頁面 |-- package.json // 項目基本信息,npm的包依賴安裝信息 .
他是項目根目錄下的一個文件,定義該項目開發所須要的各類模塊以及一些項目配置信息(如項目名稱、版本、描述、做者等)
npm init初始化的時候就會生成這個文件,而後若是須要自定義就能夠在裏面編輯
{ "name": "my-project-vue", "version": "1.0.0", "description": "A Vue.js project", "author": "XXXXX", "private": true, "scripts": { //來指定npm相關命令和通知npm執行這些命令 "dev": "node build/dev-server.js", //根據不一樣的環境執行不一樣的文件,例如在開發環境下,在命令行中運行npm run dev就至關於在執行node build/dev-server.js "build": "node build/build.js", "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run", "e2e": "node test/e2e/runner.js", "test": "npm run unit && npm run e2e", "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs" }, "dependencies": { //指定了項目運行時所依賴的模塊 "ansi-html": "0.0.7", "html-entities": "^1.2.0", "strip-ansi": "^3.0.1", "vue": "^2.1.10", //有vue "vue-router": "^2.2.0" //有vue-router }, "devDependencies": { //指定了項目開發時所依賴的模塊 "autoprefixer": "^6.7.2", "babel-core": "^6.22.1", "babel-eslint": "^7.1.1", "babel-loader": "^6.2.10", "babel-plugin-istanbul": "^3.1.2", "babel-plugin-transform-runtime": "^6.22.0", "babel-preset-es2015": "^6.22.0", "babel-preset-stage-2": "^6.22.0", "babel-register": "^6.22.0", "chai": "^3.5.0", "chalk": "^1.1.3", "chromedriver": "^2.27.2", "connect-history-api-fallback": "^1.3.0", "cross-env": "^3.1.4", "cross-spawn": "^5.0.1", "css-loader": "^0.26.1", "eslint": "^3.14.1", "eslint-config-standard": "^6.2.1", "eslint-friendly-formatter": "^2.0.7", "eslint-loader": "^1.6.1", "eslint-plugin-html": "^2.0.0", "eslint-plugin-promise": "^3.4.0", "eslint-plugin-standard": "^2.0.1", "eventsource-polyfill": "^0.9.6", "express": "^4.14.1", "extract-text-webpack-plugin": "^2.0.0-rc.2", "file-loader": "^0.10.0", "friendly-errors-webpack-plugin": "^1.1.3", "function-bind": "^1.1.0", "html-webpack-plugin": "^2.28.0", "http-proxy-middleware": "^0.17.3", "inject-loader": "^2.0.1", "json-loader": "^0.5.4", "karma": "^1.4.1", "karma-coverage": "^1.1.1", "karma-mocha": "^1.3.0", "karma-phantomjs-launcher": "^1.0.2", "karma-sinon-chai": "^1.2.4", "karma-sourcemap-loader": "^0.3.7", "karma-spec-reporter": "0.0.26", "karma-webpack": "^2.0.2", "lolex": "^1.5.2", "mocha": "^3.2.0", "nightwatch": "^0.9.12", "opn": "^4.0.2", "ora": "^1.1.0", "phantomjs-prebuilt": "^2.1.14", "selenium-server": "^3.0.1", "semver": "^5.3.0", "shelljs": "^0.7.6", "sinon": "^1.17.7", "sinon-chai": "^2.8.0", "url-loader": "^0.5.7", "vue-loader": "^10.3.0",//vue-loader等 "vue-style-loader": "^2.0.0", "vue-template-compiler": "^2.1.10", "webpack": "^2.2.1", "webpack-bundle-analyzer": "^2.2.1", "webpack-dev-middleware": "^1.10.0", "webpack-hot-middleware": "^2.16.1", "webpack-merge": "^2.6.1" }, "engines": { //顧名思義,就是告訴npm使用什麼版本的node和npm "node": ">= 4.0.0", "npm": ">= 3.0.0" } }
這個package.json文件其實很長,不過主要關注一些平常使用到的就好了,所有配置在官方:https://docs.npmjs.com/files/package.json
使用npm run dev 就會執行dev-server.js,由於這個在packet.json裏面配置了,因此須要知道dev-server.js裏面有什麼
// 檢查 Node 和 npm 版本(require指定loader) require('./check-versions')() // 獲取config目錄的默認配置,而且會默認指定index.js文件 var config = require('../config') if (!process.env.NODE_ENV) { process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV) } // 一個能夠強制打開瀏覽器並跳轉到指定 url 的插件 var opn = require('opn') // 使用 NodeJS 自帶的文件路徑工具 var path = require('path') var express = require('express') // 使用 webpack var webpack = require('webpack') // http-proxy能夠實現轉發全部請求代理到後端真實API地址,以實現先後端開發徹底分離 // 使用 proxyTable var proxyMiddleware = require('http-proxy-middleware') // 判斷是否使用 dev 環境的 webpack 配置 var webpackConfig = process.env.NODE_ENV === 'testing' ? require('./webpack.prod.conf') : require('./webpack.dev.conf') // default port where dev server listens for incoming traffic var port = process.env.PORT || config.dev.port // automatically open browser, if not set will be false var autoOpenBrowser = !!config.dev.autoOpenBrowser // Define HTTP proxies to your custom API backend // https://github.com/chimurai/http-proxy-middleware // 使用 config.dev.proxyTable 的配置做爲 proxyTable 的代理配置 var proxyTable = config.dev.proxyTable // 使用 express 啓動一個服務 var app = express() // 啓動 webpack 進行編譯 var compiler = webpack(webpackConfig) // 啓動 webpack-dev-middleware,將 編譯後的文件暫存到內存中 var devMiddleware = require('webpack-dev-middleware')(compiler, { publicPath: webpackConfig.output.publicPath, quiet: true }) / 啓動 webpack-hot-middleware,也就是咱們常說的 Hot-reload 熱加載 var hotMiddleware = require('webpack-hot-middleware')(compiler, { log: () => {} }) // force page reload when html-webpack-plugin template changes compiler.plugin('compilation', function (compilation) { compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { hotMiddleware.publish({ action: 'reload' }) cb() }) }) // proxy api requests Object.keys(proxyTable).forEach(function (context) { var options = proxyTable[context] if (typeof options === 'string') { options = { target: options } } app.use(proxyMiddleware(options.filter || context, options)) }) // handle fallback for HTML5 history API app.use(require('connect-history-api-fallback')()) // serve webpack bundle output // 將暫存到內存中的 webpack 編譯後的文件掛在到 express 服務上 app.use(devMiddleware) // enable hot-reload and state-preserving // compilation error display // 將 Hot-reload 掛在到 express 服務上 app.use(hotMiddleware) // serve pure static assets // 拼接 static 文件夾的靜態資源路徑 var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) // 爲靜態資源提供響應服務 app.use(staticPath, express.static('./static')) var uri = 'http://localhost:' + port devMiddleware.waitUntilValid(function () { console.log('> Listening at ' + uri + '\n') }) // 讓咱們這個 express 服務監聽 port 的請求,而且將此服務做爲 dev-server.js 的接口暴露 module.exports = app.listen(port, function (err) { if (err) { console.log(err) return } // when env is testing, don't need open it if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') { opn(uri) } })
備註:
加載器(Loaders) 引用
loader 是對應用程序中資源文件進行轉換。它們是(運行在 Node.js 中的) 函數,能夠將資源文件做爲參數的來源,而後返回新的資源文件。
例如,你可使用 loader 告訴 webpack 加載 CSS 文件,或者將 TypeScript 轉爲 JavaScript。
loader 解析相似於模塊。loader 模塊須要導出(module.export)一個函數,而且使用兼容 Node.js 的 JavaScript 編寫。在一般狀況下,你可使用 npm 管理 loader,可是你也能夠在應用程序中將 loader 做爲文件使用。
能夠看到webpack其實使用了node.js的express網頁服務器來進行處理網頁相關的數據,至關於使用一個相似apache這樣的web服務器來執行解析html等文件,只是這裏換成了node.js的express,而且能夠執行js文件
須要注意一點:require的時候,若是沒有指定文件的話,有一些狀況是會自定指定該目錄下的index.js文件的,詳情參考
dev-server.js的環境配置是調用webpack.dev.conf.js的,因此也須要看看了解一下
// 使用一些小工具 var utils = require('./utils') // 使用 webpack var webpack = require('webpack') // 獲取config目錄的默認配置,而且會默認指定index.js文件 var config = require('../config') // 使用 webpack 配置合併插件 var merge = require('webpack-merge') // 加載 webpack.base.conf var baseWebpackConfig = require('./webpack.base.conf') // 使用 html-webpack-plugin 插件,這個插件能夠幫咱們自動生成 html 而且注入到 .html 文件中 var HtmlWebpackPlugin = require('html-webpack-plugin') var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') // add hot-reload related code to entry chunks // 將 Hol-reload 相對路徑添加到 webpack.base.conf 的 對應 entry 前 Object.keys(baseWebpackConfig.entry).forEach(function (name) { baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) }) // 將咱們 webpack.dev.conf.js 的配置和 webpack.base.conf.js 的配置合併 module.exports = merge(baseWebpackConfig, { module: { // 使用 styleLoaders rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) }, // cheap-module-eval-source-map is faster for development devtool: '#cheap-module-eval-source-map', plugins: [ // definePlugin 接收字符串插入到代碼當中, 因此你須要的話能夠寫上 JS 的字符串 new webpack.DefinePlugin({ 'process.env': config.dev.env }), // https://github.com/glenjamin/webpack-hot-middleware#installation--usage // HotModule 插件在頁面進行變動的時候只會重回對應的頁面模塊,不會重繪整個 html 文件 new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin(), // https://github.com/ampedandwired/html-webpack-plugin // 將 index.html 做爲入口,注入 html 代碼後生成 index.html文件 new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.html', inject: true }), new FriendlyErrorsPlugin() ] })
webpack.dev.conf.js會導入webpack.base.conf.js,而且會進行合併配置
var path = require('path') var utils = require('./utils') var config = require('../config') //加載了vue-loader,主要是咱們這個項目是vue-cli構建的,因此相對配置也所特別 var vueLoaderConfig = require('./vue-loader.conf') var eslintFriendlyFormatter = require('eslint-friendly-formatter') function resolve (dir) { return path.join(__dirname, '..', dir) } module.exports = { entry: { //主要入口 app: './src/main.js' }, output: { path: config.build.assetsRoot,// 編譯輸出的根路徑 filename: '[name].js', //輸出的文件名 //判斷是否dev環境來處理靜態資源 publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath }, resolve: { extensions: ['.js', '.vue', '.json'], modules: [ resolve('src'), resolve('node_modules') ], alias: { // 默認路徑代理,例如 import Vue from 'vue',會自動到 'vue/dist/vue.common.js'中尋找 'vue$': 'vue/dist/vue.common.js', 'src': resolve('src'), 'assets': resolve('src/assets'), 'components': resolve('src/components') } }, module: { rules: [ //對不一樣文件使用不一樣的loader { test: /\.(js|vue)$/, loader: 'eslint-loader', enforce: "pre", include: [resolve('src'), resolve('test')], options: { formatter: eslintFriendlyFormatter } }, { //使用vue-loader解析.vue的文件 test: /\.vue$/, loader: 'vue-loader', options: vueLoaderConfig }, { //普通的js文件使用babel-loader解析 test: /\.js$/, loader: 'babel-loader', include: [resolve('src'), resolve('test')] }, { //其餘差很少相似 test: /\.json$/, loader: 'json-loader' }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', query: { limit: 10000, name: utils.assetsPath('img/[name].[hash:7].[ext]') } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', query: { limit: 10000, name: utils.assetsPath('fonts/[name].[hash:7].[ext]') } } ] } }
// see http://vuejs-templates.github.io/webpack for documentation. var path = require('path') module.exports = { build: { //若是是build的話就使用production環境配置 env: require('./prod.env'), index: path.resolve(__dirname, '../dist/index.html'), assetsRoot: path.resolve(__dirname, '../dist'), assetsSubDirectory: 'static', assetsPublicPath: '/', productionSourceMap: true, // Gzip off by default as many popular static hosts such as // Surge or Netlify already gzip all static assets for you. // Before setting to `true`, make sure to: // npm install --save-dev compression-webpack-plugin productionGzip: false, productionGzipExtensions: ['js', 'css'], // Run the build command with an extra argument to // View the bundle analyzer report after build finishes: // `npm run build --report` // Set to `true` or `false` to always turn it on or off bundleAnalyzerReport: process.env.npm_config_report }, dev: { // dev的話就使用dev的環境配置 env: require('./dev.env'), port: 8080, autoOpenBrowser: true, assetsSubDirectory: 'static', assetsPublicPath: '/', proxyTable: {}, // CSS Sourcemaps off by default because relative paths are "buggy" // with this option, according to the CSS-Loader README // (https://github.com/webpack/css-loader#sourcemaps) // In our experience, they generally work as expected, // just be aware of this issue when enabling this option. cssSourceMap: false } }
能夠看到dev是沒有生成index.html之類的文件的,那是由於dev的話會直接在內存處理,方便調試,也能夠利用熱加載直接更新
參考: