若是你如今正在使用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的话会直接在内存处理,方便调试,也能够利用热加载直接更新
参考: