第一步 node环境安装

1.1 如果本机没有安装node运行环境,请下载node 安装包进行安装
1.2 如果本机已经安装node的运行换,请更新至最新的node 版本
下载地址:https://nodejs.org/en/ 或者 http://nodejs.cn/

第二步 node环境检测

1.2.1 在终端输入 node -v
如果输出版本号,说明我们安装node 环境成功
随便我们可以查看 npm 的 版本号,安装node之后是自带npm的
可以使用 npm -v

第三步 vue-cli脚手架安装

2.1 如果访问外网比较慢,可以使用淘宝的镜像 https://npm.taobao.org/
打开命令终端 npm install -g cnpm --registry=https://registry.npm.taobao.org
回车之后,我就可以可以快乐的用 cnpm 替代 npm
2.2 初始化项目 vue init webpack vue-demo

输入命令后,会询问我们几个简单的选项,我们根据自己的需要进行填写就可以了。

 

Project name :项目名称 ,如果不需要更改直接回车就可以了。注意:这里不能使用大写,所以我把名称改成了vueclitest
Project description:项目描述,默认为A Vue.js project,直接回车,不用编写。
Author:作者,如果你有配置git的作者,他会读取。
Install  vue-router? 是否安装vue的路由插件,我们这里需要安装,所以选择Y
Use ESLint to lint your code? 是否用ESLint来限制你的代码错误和风格。(这里的话建议是需要的),输入y,如果你是大型团队开发,是一定要进行配置。然后会出现选择eslint源,这个可以根据自己的需要进行选择,后期也可以自己进行配置。
setup unit tests with  Karma + Mocha? 是否需要安装单元测试工具Karma+Mocha,我们这里不需要,所以输入n。
Setup e2e tests with Nightwatch?是否安装e2e来进行用户行为模拟测试,我们这里不需要,所以输入n

2.3 进入 cd vue-demo

2.4 执行 npm install

2.5 接下来执行 npm run dev

 

下面的内容是整合,已经得到了博主的转载授权

博主:静静是小花
博客链接:
https://www.cnblogs.com/hongdiandian

 

 

vue-cli脚手架目录一览

 

最近在学习vue,看的稀里糊涂。今天从头开始,把cli配置的vue项目目录和配置文件搞清楚。

先看看整个项目目录结构:

 

再看看build文件夹下相关文件及目录:

 

config文件夹下目录和文件:

接下来说说vue-cli项目中页面相关的主要文件^o^

首先是index.html:

说明:一般只定义一个空的根节点,在main.js里面定义的实例将挂载在#app节点下,内容通过vue组件填充。

 

 

App.vue文件:

说明:app.vue是项目的主组件,所有页面都是在app.vue下切换的。一个标准的vue文件,分为三部分。

第一装写html代码在<template></template>中,一般在此下面只能定义一个根节点;

第二<script></script>标签;

第三<style scoped></style>用来写样式,其中scoped表示。该style作用于只在当前组件的节点及其子节点,但是不包含子组件呦。

<router-view></router-view>是子路由视图,后面的路由页面都显示在此处,相当于一个指示标,指引显示哪个页面。

 

main.js:

说明:入口文件来着,主要作用是初始化vue实例并使用需要的插件。比如下面引用了4个插件,但只用了app(components里面是引用的插件)。

 

router下面的index.js文件:路由配置文件。

说明:定义了三个路由,分别是路径为/,路径为/msg,路径为/detail。后续会详细说明,因为我也是才学好多东西不懂,囧。

 

vue-cli脚手架之build文件夹上半部

 

 

好,接下来一起分析分析配置文件^o^。

 

build.js作用:命令npm run build的入口配置文件,主要用于生产环境。

 

build.js中具体含义标注(vue-cli脚手架官方文件解释,大家可自行定制这里面的内容):

 

check-version.js,文件有点点长这里直接贴代码:

 1 'use strict' //js严格运行模式
 2 const chalk = require('chalk') //导入chalk模块,const声明一个常量
 3 const semver = require('semver') //同上
 4 const packageConfig = require('../package.json') //导入package.json文件
 5 const shell = require('shelljs')//shelljs插件,执行unix系统命令
 6 
 7 function exec (cmd) {
 8 //脚本可以通过child_process模块新建子进程,从而执行Unix系统命令
 9   return require('child_process').execSync(cmd).toString().trim()//将cmd参数传递的值转换成前后没有空格的字符串,也就是版本号
10 }
11 //声明常量数组,数组内容为有关node相关信息的对象
12 const versionRequirements = [
13   {
14     name: 'node',//对象名称为node
15     currentVersion: semver.clean(process.version),//使用semver插件,把版本信息转换成规定格式
16     versionRequirement: packageConfig.engines.node//规定package.json中engines选项的node版本信息
17   }
18 ]
19 
20 if (shell.which('npm')) {//which为linux指令,在$path规定的路径下查找符合条件的文件
21   versionRequirements.push({
22     name: 'npm',
23     currentVersion: exec('npm --version'),//调用npm --version命令,并且把参数返回给exec函数获取纯净版本
24     versionRequirement: packageConfig.engines.npm//规定package.json中engines选项的node版本信息
25   })
26 }
27 
28 module.exports = function () {
29   const warnings = []
30 
31   for (let i = 0; i < versionRequirements.length; i++) {
32     const mod = versionRequirements[i]
33   //如果版本号不符合package.json文件中指定的版本号,就执行warning.push...
34     if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
35       warnings.push(mod.name + ': ' +
36         chalk.red(mod.currentVersion) + ' should be ' +
37         chalk.green(mod.versionRequirement)
38         //当前版本号用红色标识,要求版本号用绿色标识
39       )
40     }
41   }
42 
43   if (warnings.length) {//如果为真,则打印提示用户升级新版本
44     console.log('')
45     console.log(chalk.yellow('To use this template, you must update following to modules:'))
46     console.log()
47 
48     for (let i = 0; i < warnings.length; i++) {
49       const warning = warnings[i]
50       console.log('  ' + warning)
51     }
52 
53     console.log()
54     process.exit(1)
55   }
56 }

 

utils.js还是需要贴代码,太长了:

 
'use strict'//js严格模式执
const path = require('path')//导入path模
const config = require('../config')//引入config目录下的index.js配置文件
const ExtractTextPlugin = require('extract-text-webpack-plugin')//一个插件,抽离css样式,防止将样式打包在js中引起样式加载错乱
const packageConfig = require('../package.json')
//导出assetsPath
exports.assetsPath = function (_path) {
//如果是生产环境,则assetsSubDirectory的值为index.js文件中的assetsSubDirectory的值,否则...
  const assetsSubDirectory = process.env.NODE_ENV === 'production'
    ? config.build.assetsSubDirectory
    : config.dev.assetsSubDirectory

  return path.posix.join(assetsSubDirectory, _path)//path.join返回绝对路径(在电脑上的实际位置);path.posix.join返回相对路径
}
//cssloaders相关配置
exports.cssLoaders = function (options) {
  options = options || {}

  const cssLoader = {
    loader: 'css-loader',//loader还是看看webpack官方解释,处理除js之外的文件?
    options: {//传递参数给loader
      sourceMap: options.sourceMap//是否开启cssmap,默认为false
    }
  }
//postcss-loader相关
  const postcssLoader = {
    loader: 'postcss-loader',
    options: {
      sourceMap: options.sourceMap
    }
  }

  // generate loader string to be used with extract text plugin
  function generateLoaders (loader, loaderOptions) {
    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]//是否使用postCss

    if (loader) {
      loaders.push({
        loader: loader + '-loader',//加载对应loader
        options: Object.assign({}, loaderOptions, {//object.assign浅拷贝合并对象
          sourceMap: options.sourceMap
        })
      })
    }

    // Extract CSS when that option is specified
    //
    if (options.extract) {
      return ExtractTextPlugin.extract({
        use: loaders,
        fallback: 'vue-style-loader'
      })
    } else {
      return ['vue-style-loader'].concat(loaders)
    }
//返回最终读取和导入loader
  }

  // https://vue-loader.vuejs.org/en/configurations/extract-css.html
  return {
    css: generateLoaders(),//css对应vue-style-loader和css-loader
    postcss: generateLoaders(),//postcss对应vue-style-loader和less-loader
    less: generateLoaders('less'),//less对应...(同上)
    sass: generateLoaders('sass', { indentedSyntax: true }),
    scss: generateLoaders('sass'),
    stylus: generateLoaders('stylus'),
    styl: generateLoaders('stylus')
  }
}

// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
  const output = []
  const loaders = exports.cssLoaders(options)
//生成的各种css文件的loader对象
  for (const extension in loaders) {
    const loader = loaders[extension]//提取每一种文件的loader
    output.push({
      test: new RegExp('\.' + extension + '$'),
      use: loader
    })
  }

  return output
}

exports.createNotifierCallback = () => {
  const notifier = require('node-notifier')//导入模块,用于node.js模块发送跨平台系统通知

  return (severity, errors) => {
    if (severity !== 'error') return

    const error = errors[0]
    const filename = error.file && error.file.split('!').pop()

    notifier.notify({
      title: packageConfig.name,//发生错误时的通知标题
      message: severity + ': ' + error.name,
      subtitle: filename || '',
      icon: path.join(__dirname, 'logo.png')//发生错误时的通知图标
    })
  }
}

 

vue-loader.config.js

 

这篇好长啊,先结束了,下一篇再介绍重要的几个文件:webpack.base.conf.js     webpack.dev.conf.js     webpack.prod.conf.js     webpack.test.conf.js

vue-cli脚手架之webpack.base.conf.js

 

webpack相关的重要配置文件将在这一节给出。webpack水很深啊^o^,在此先弄清楚原配文件内容的含义,后续可以自己根据实际情况配置。

 

webpack.base.conf.js:配置vue开发环境的webpack配置,处理各种文件(js啊、css啊、html啊...)

'use strict'//js严格模式执行
const path = require('path')//引入node.js路径模块
const utils = require('./utils')//引入utils工具模块,主要处理css-loader和vue-style-loader
const config = require('../config')//引入config文件夹下的index.js文件
const vueLoaderConfig = require('./vue-loader.conf')//引入vue-loader工具模块

function resolve (dir) {//返回当前目录的平行目录的路径
  return path.join(__dirname, '..', dir)
}

module.exports = {
  context: path.resolve(__dirname, '../'),
  entry: {//输入
    app: './src/main.js'//入口文件为main.js
  },
  output: {//输出
    path: config.build.assetsRoot,//打包后文件输出路径,看看自己的index.js中build配置中的assetsRoot是啥目录
    filename: '[name].js',//输出文件名称默认使用原名
    publicPath: process.env.NODE_ENV === 'production'//真正的文件引用路径,请看自己的index.js中build配置中写的啥
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {//决定要做的事情
    extensions: ['.js', '.vue', '.json'],//省略扩展名,也就是说当使用.js .vue .json文件导入可以省略后缀名
    alias: {
      'vue$': 'vue/dist/vue.esm.js',//$符号指精确匹配,路径和文件名要详细
      '@': resolve('src'),//resolve('src‘)//resolve('src')指的是项目根目录中的src文件夹目录,导入文件的时候路径可以这样简写 import somejs from "@/some.js"就可以导入指定文件
    }
  },
//用来解析不同模块
  module: {
    rules: [
      {
        test: /.vue$/,//正则表达式,表示当前loader能检测.vue类型的文件(分析这个正则:/标记正则表达式的开始和结束,指的是在开始和结尾处哦,否则要使用/就得转义/;.表示.,此处的将.标记为原意字符;$是正则表达式的结束?这个我不知道...)
        loader: 'vue-loader',//对vue文件使用vue-loader,该loader是vue单文件组件的实现核心,解析.vue文件
        options: vueLoaderConfig//将vueLoaderConfig当做参数传递给vue-loader,解析css相关文件
      },
      {
        test: /.js$/,
        loader: 'babel-loader',//对js文件使用babel-loader转码,该插件用来解析es6等代码
        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]//指明src文件夹 test文件夹 client文件夹下的js文件要使用该loader
      },
      {
        test: /.(png|jpe?g|gif|svg)(?.*)?$/,//这些格式结尾的图片文件
        loader: 'url-loader',//图片文件使用url-loader插件,将图片转为base64格式字符串
        options: {
          limit: 10000,//10000个字节以下的文件才用来转为dataUrl
          name: utils.assetsPath('img/[name].[hash:7].[ext]')//超过10000字节的图片,就按照制定规则设置生成的图片名称,可以看到用了7位hash码来标记,.ext文件是一种索引式文件系统
        }
      },
      {
        test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/,//音频 视频类文件
        loader: 'url-loader',//也是用url-loader
        options: {
          limit: 10000,//10000个字节以下的文件才进行转换
          name: utils.assetsPath('media/[name].[hash:7].[ext]')//这个name到底是给谁起的啊喂,给超过limit字节限制的文件起的
        }
      },
      {
        test: /.(woff2?|eot|ttf|otf)(?.*)?$/,//处理字体相关
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      },
      /*添加sass开始*/
      {
        test:/.(woff2?|eot|ttf|otf)(?.*)?$/,//这个可以在vue组件中用sass scss等...
        loaders:['style','css','sass'],
      }
      /*添加sass结束*/
    ]
  },
  node: {//一个对象,每个属性都是node.js全局变量或模块的名称,value为empty表示提供空对象
    // prevent webpack from injecting useless setImmediate polyfill because Vue
    // source contains it (although only uses it if it's native).
    setImmediate: false,//false表示什么都不提供,话说参数setImmediate表示异步递归???需要查阅node文档了
    // prevent webpack from injecting mocks to Node native modules
    // that does not make sense for the client
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  }
}

vue-cli脚手架之webpack.dev.conf.js

 

webpack.dev.conf.js  开发环境模式配置文件:

'use strict'//js按照严格模式执行
const utils = require('./utils')//导入utils.js
const webpack = require('webpack')//使用webpack来使用webpack内置插件
const config = require('../config')//config文件夹下index.js文件
const merge = require('webpack-merge')//引入webpack-merge插件用来合并webpack配置对象,也就是说可以把webpack配置文件拆分成几个小的模块,然后合并
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')//导入webpack基本配置
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')//生成html文件
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')//获取port

const HOST = process.env.HOST//process.env属性返回一个对象,包含了当前shell的所有环境变量。这句取其中的host文件?
const PORT = process.env.PORT && Number(process.env.PORT)//获取所有环境变量下的端口?

//合并模块,第一个参数是webpack基本配置文件webpack.base.conf.js中的配置
const devWebpackConfig = merge(baseWebpackConfig, {
  module: {
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })//创建模块时匹配请求的规则数组,这里调用了utils中的配置模板styleLoaders
  },
  // cheap-module-eval-source-map is faster for development
  devtool: config.dev.devtool,//debtool是开发工具选项,用来指定如何生成sourcemap文件,cheap-module-eval-source-map此款soucemap文件性价比最高

  // these devServer options should be customized in /config/index.js
  devServer: {//webpack服务器配置
    clientLogLevel: 'warning',//使用内联模式时,在开发工具的控制台将显示消息,可取的值有none error warning info
    historyApiFallback: {//当使用h5 history api时,任意的404响应都可能需要被替代为index.html,通过historyApiFallback:true控制;通过传入一个对象,比如使用rewrites这个选项进一步控制
      rewrites: [
        { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
      ],
    },
    hot: true,//是否启用webpack的模块热替换特性。这个功能主要是用于开发过程中,对生产环境无帮助。效果上就是界面无刷新更新。
    contentBase: false, // since we use CopyWebpackPlugin.这里禁用了该功能。本来是告诉服务器从哪里提供内容,一半是本地静态资源。
    compress: true,//一切服务是否都启用gzip压缩
    host: HOST || config.dev.host,//指定一个host,默认是localhost。如果有全局host就用全局,否则就用index.js中的设置。
    port: PORT || config.dev.port,//指定端口
    open: config.dev.autoOpenBrowser,//是否在浏览器开启本dev server
    overlay: config.dev.errorOverlay//当有编译器错误时,是否在浏览器中显示全屏覆盖。
      ? { warnings: false, errors: true }
      : false,
    publicPath: config.dev.assetsPublicPath,//此路径下的打包文件可在浏览器中访问
    proxy: config.dev.proxyTable,//如果你有单独的后端开发服务器api,并且希望在同域名下发送api请求,那么代理某些URL会很有用。
    quiet: true, // necessary for FriendlyErrorsPlugin  启用 quiet 后,除了初始启动信息之外的任何内容都不会被打印到控制台。这也意味着来自 webpack 的错误或警告在控制台不可见。
    watchOptions: {//webpack 使用文件系统(file system)获取文件改动的通知。在某些情况下,不会正常工作。例如,当使用 Network File System (NFS) 时。Vagrant 也有很多问题。在这些情况下使用轮询。
      poll: config.dev.poll,//是否使用轮询
    }
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': require('../config/dev.env')
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
    new webpack.NoEmitOnErrorsPlugin(),
    // https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({//模块HtmlWebpackPlugin
      filename: 'index.html',//生成的文件的名称
      template: 'index.html',//可以指定模块html文件
      inject: true//在文档上没查到这个选项 不知道干嘛的。。。
    }),
    // copy custom static assets
    new CopyWebpackPlugin([//模块CopyWebpackPlugin  将单个文件或整个文件复制到构建目录
      {
        from: path.resolve(__dirname, '../static'),//将static文件夹及其子文件复制到
        to: config.dev.assetsSubDirectory,
        ignore: ['.*']//这个没翻译好,百度翻译看不懂,请自己查文档。。。
      }
    ])
  ]
})
//webpack将运行由配置文件导出的函数,并且等待promise返回,便于需要异步地加载所需的配置变量。
module.exports = new Promise((resolve, reject) => {
  portfinder.basePort = process.env.PORT || config.dev.port
  portfinder.getPort((err, port) => {
    if (err) {
      reject(err)
    } else {
      // publish the new Port, necessary for e2e tests
      process.env.PORT = port
      // add port to devServer config
      devWebpackConfig.devServer.port = port

      // Add FriendlyErrorsPlugin
      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({//出错友好处理插件
        compilationSuccessInfo: {//build成功的话会执行者块
          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
        },
        onErrors: config.dev.notifyOnErrors//如果出错就执行这块,其实是utils里面配置好的提示信息
        ? utils.createNotifierCallback()
        : undefined
      }))

      resolve(devWebpackConfig)
    }
  })
})

vue-cli脚手架之webpack.prod.conf.js

 

webpack.prod.conf.js 生产环境配置文件:

'use strict'//js严格模式执行
const path = require('path')//这个模块是发布到NPM注册中心的NodeJS“路径”模块的精确副本
const utils = require('./utils')//utils.js文件
const webpack = require('webpack')//webpack模块
const config = require('../config')//config文件夹下的index.js  是不是很神奇?
const merge = require('webpack-merge')//合并数组、对象为一个新的对象的模块
const baseWebpackConfig = require('./webpack.base.conf')//webpack.base.conf.js
const CopyWebpackPlugin = require('copy-webpack-plugin')//拷贝文件和文件夹模块
const HtmlWebpackPlugin = require('html-webpack-plugin')//为html文件中引入的外部资源(比如script/link等)动态添加每次compile后的hash,保证文件名不重复的好处是防止引用缓存文件导致修改暂未生效;可生成创建html入口文件
const ExtractTextPlugin = require('extract-text-webpack-plugin')//抽离css样式,防止将样式打包到js中引起加载错乱
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')//压缩css插件
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')//压缩js代码。

const env = require('../config/prod.env')//设置为生产环境production
//merge方法合并模块对象,在这个文件里是将基础配置webpack.base.conf.js和生产环境配置合并
const webpackConfig = merge(baseWebpackConfig, {
  module: {//模块配置
    rules: utils.styleLoaders({//原版注释Generate loaders for standalone style files (outside of .vue)生成独立的样式文件装载机
      sourceMap: config.build.productionSourceMap,//设置sourceMap
      extract: true,//
      usePostCSS: true
    })
  },
  devtool: config.build.productionSourceMap ? config.build.devtool : false,//指定是否使用sourceMap
  output: {//指定输出
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),//编译输出的js文件存放在js文件夹下,命名规则添加hash计算
    /**
     * 打包require.ensure方法中引入的模块,如果该方法中没有引入任何模块则不会生成任何chunk块文件
     *
     * 比如在main.js文件中,require.ensure([],function(require){alert(11);}),这样不会打包块文件
     * 只有这样才会打包生成块文件require.ensure([],function(require){alert(11);require('./greeter')})
     * 或者这样require.ensure(['./greeter'],function(require){alert(11);})
     * chunk的hash值只有在require.ensure中引入的模块发生变化,hash值才会改变
     * 注意:对于不是在ensure方法中引入的模块,此属性不会生效,只能用CommonsChunkPlugin插件来提取
     */
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new webpack.DefinePlugin({
      'process.env': env
    }),
    new UglifyJsPlugin({//压缩js代码的插件  具体可以去npm查一下这个插件怎么用以及能设置哪些参数
      uglifyOptions: {
        compress: {
          warnings: false
        }
      },
      sourceMap: config.build.productionSourceMap,//是否生成sourceMap
      parallel: true
    }),
    // extract css into its own file
    new ExtractTextPlugin({
      filename: utils.assetsPath('css/[name].[contenthash].css'),
      // Setting the following option to `false` will not extract CSS from codesplit chunks.
      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
      // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
      allChunks: true,
    }),
    // Compress extracted CSS. We are using this plugin so that possible
    // duplicated CSS from different components can be deduped.
    new OptimizeCSSPlugin({
      cssProcessorOptions: config.build.productionSourceMap
        ? { safe: true, map: { inline: false } }
        : { safe: true }
    }),
    // generate dist index.html with correct asset hash for caching.
    // you can customize output by editing /index.html
    // see https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: config.build.index,
      template: 'index.html',
      inject: true,
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      },
      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
      chunksSortMode: 'dependency'
    }),
    // keep module.id stable when vendor modules does not change
    new webpack.HashedModuleIdsPlugin(),
    // enable scope hoisting
    new webpack.optimize.ModuleConcatenationPlugin(),
    // split vendor js into its own file
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks (module) {
        // any required modules inside node_modules are extracted to vendor
        return (
          module.resource &&
          /.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      }
    }),
    // extract webpack runtime and module manifest to its own file in order to
    // prevent vendor hash from being updated whenever app bundle is updated
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    }),
    // This instance extracts shared chunks from code splitted chunks and bundles them
    // in a separate chunk, similar to the vendor chunk
    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
    new webpack.optimize.CommonsChunkPlugin({
      name: 'app',
      async: 'vendor-async',
      children: true,
      minChunks: 3
    }),

    // copy custom static assets
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
  ]//添加插件,是webpack功能更丰富
})
//是否允许压缩?
if (config.build.productionGzip) {
  const CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
        '\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}

if (config.build.bundleAnalyzerReport) {
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}

module.exports = webpackConfig

vue-cli脚手架之webpack.test.conf.js

 

webpack单元测试配置:

// This is the webpack config used for unit tests.

var utils = require('./utils')//utils.js文件导入
var webpack = require('webpack')//webpack模块导入
var merge = require('webpack-merge')//合并模块插件
var baseConfig = require('./webpack.base.conf')//导入基础配置webpack.base.conf.js

var webpackConfig = merge(baseConfig, {
  // use inline sourcemap for karma-sourcemap-loader
  module: {
    rules: utils.styleLoaders()
  },
  devtool: '#inline-source-map',
  plugins: [
    new webpack.DefinePlugin({
      'process.env': require('../config/test.env')//指定环境为测试环境  test.env中设置了NODE_ENV=‘testing’
    })
  ]
})

// no need for app entry during tests
delete webpackConfig.entry

module.exports = webpackConfig

vue-cli脚手架之package.json

 

package.json文件配置及其含义,这个是vue-cli自动生成的文件,先贴一张代码及其含义:

{
  "name": "secondproject",//模块名称
  "version": "1.0.0",//模块版本
  "description": "A Vue.js project",//对模块的描述
  "author": "datura",//作者是谁
  "private": true,//如果值为true,npm将拒绝发布它
  "scripts": {//值是一个对象,里面指定了项目的生命周期各个环节需要执行的命令
    "dev": "node build/dev-server.js",//这个就是在命令行执行npm run dev,其实是运行dev-server.js文件
    "build": "node build/build.js",//build命令(有一个钩子的概念:比如这个build有prebuild和postbuild
    "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",//babel是一个编译器,可以把ES6编译成ES5.,这句先是设置编译环境为test环境下;karma是一个运行时,它产生一个web服务环境来运行项目代码,并执行测试。..
    "e2e": "node test/e2e/runner.js",//e2e模拟用户行为的测试,端到端测试
    "test": "npm run unit && npm run e2e"//执行单元测试和e2e测试
  },//关于npm钩子:通常程序只能处理来自内部的消息,如果希望对外部发来的消息也能拦截处理,就需要用到Hook技术。比如想在run build之前自动执行点任务,可以将其写在run prebuild标签里;postbuild在build之后自动执行
  "dependencies": {//配置模块依赖的模块列表,key是模块名称,value是版本范围,版本范围是一个字符,可被一个或多个空格分割。
    "router": "^1.3.0",//路由版本
    "vue": "^2.2.1",//vue版本
    "vue-resource": "^1.2.1",//一个插件,通过xmlHttpRequest或jsonp发起请求并处理响应。
    "vue-router": "^2.3.0"//
  },
  "devDependencies": {//这里写的依赖是用于开发环境的,不发布到生产环境。
    "autoprefixer": "^6.7.2",
    "babel-core": "^6.22.1",
    "babel-loader": "^6.2.10",
    "babel-plugin-transform-runtime": "^6.22.0",
    "babel-preset-latest": "^6.22.0",
    "babel-preset-stage-2": "^6.22.0",
    "babel-register": "^6.22.0",
    "chalk": "^1.1.3",
    "connect-history-api-fallback": "^1.3.0",
    "copy-webpack-plugin": "^4.0.1",
    "css-loader": "^0.26.1",
    "eventsource-polyfill": "^0.9.6",
    "express": "^4.14.1",
    "extract-text-webpack-plugin": "^2.0.0",
    "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",
    "webpack-bundle-analyzer": "^2.2.1",
    "cross-env": "^3.1.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",
    "chai": "^3.5.0",
    "sinon": "^1.17.7",
    "sinon-chai": "^2.8.0",
    "inject-loader": "^2.0.1",
    "babel-plugin-istanbul": "^3.1.2",
    "phantomjs-prebuilt": "^2.1.14",
    "chromedriver": "^2.27.2",
    "cross-spawn": "^5.0.1",
    "nightwatch": "^0.9.12",
    "selenium-server": "^3.0.1",
    "semver": "^5.3.0",
    "opn": "^4.0.2",
    "optimize-css-assets-webpack-plugin": "^1.3.0",
    "ora": "^1.1.0",
    "rimraf": "^2.6.0",
    "url-loader": "^0.5.7",
    "vue-loader": "^11.0.0",
    "vue-style-loader": "^2.0.0",
    "vue-template-compiler": "^2.2.1",
    "webpack": "^2.2.1",
    "webpack-dev-middleware": "^1.10.0",
    "webpack-hot-middleware": "^2.16.1",
    "webpack-merge": "^2.6.1"
  },
  "engines": {//指定项目运行的node或者npm版本范围,有点像安卓的指定开发level哦
    "node": ">= 4.0.0",
    "npm": ">= 3.0.0"
  },
  "browserlist": [//在不同的前端工具之间共享目标浏览器的库,确定哪些支持哪些版本的浏览器
    "> 1%",//全球有超1%的人使用的浏览器
    "last 2 versions",//根据CanIUse.com追踪的最后两个版本的所有浏览器
    "not ie <= 8"//排除先前查询选择的浏览器    天啦噜 英语不好是硬伤 不知怎么翻译好理解
  ]
}

这块想插播一篇文章,关于package.json文件讲的很详细很好理解,可以作为参考手册收藏啦。

附上原文链接:https://www.cnblogs.com/tzyy/p/5193811.html#_h1_0

 

概述

本文档是自己看官方文档的理解+翻译,内容是package.json配置里边的属性含义。package.json必须是一个严格的json文件,而不仅仅是js里边的一个对象。其中很多属性可以通过npm-config来生成。

 

name

package.json中最重要的属性是name和version两个属性,这两个属性是必须要有的,否则模块就无法被安装,这两个属性一起形成了一个npm模块的唯一标识符。模块中内容变更的同时,模块版本也应该一起变化。
name属性就是你的模块名称,下面是一些命名规则:

  • name必须小于等于214个字节,包括前缀名称在内(如 xxx/xxxmodule)。
  • name不能以"_"或"."开头
  • 不能含有大写字母
  • name会成为url的一部分,不能含有url非法字符
    下面是官网文档的一些建议:
  • 不要使用和node核心模块一样的名称
  • name中不要含有"js"和"node"。 It's assumed that it's js, since you're writing a package.json file, and you can specify the engine using the "engines" field. (See below.)
  • name属性会成为模块url、命令行中的一个参数或者一个文件夹名称,任何非url安全的字符在name中都不能使用,也不能以"_"或"."开头
  • name属性也许会被写在require()的参数中,所以最好取个简短而语义化的值。
  • 创建一个模块前可以先到后边的网址查查name是否已经被占用. https://www.npmjs.com/

name属性可以有一些前缀如 e.g. @myorg/mypackage.在npm-scope(7)的文档中可以看到详细说明

 

version

version必须可以被npm依赖的一个node-semver模块解析。具体规则见下面的dependencies模块

 

description

一个描述,方便别人了解你的模块作用,搜索的时候也有用。

 

keywords

一个字符串数组,方便别人搜索到本模块

 

homepage

项目主页url
注意: 这个项目主页url和url属性不同,如果你填写了url属性,npm注册工具会认为你把项目发布到其他地方了,获取模块的时候不会从npm官方仓库获取,而是会重定向到url属性配置的地址。
(原文档中用了 spit(吐)这个单词,作者表示他不是在开玩笑:)

 

bugs

填写一个bug提交地址或者一个邮箱,被你的模块坑到的人可以通过这里吐槽,例如:

{ "url" : "https://github.com/owner/project/issues"
, "email" : "project@hostname.com"
}

url和email可以任意填或不填,如果只填一个,可以直接写成一个字符串而不是对象。如果填写了url,npm bugs命令会使用这个url。

 

license

你应该为你的模块制定一个协议,让用户知道他们有何权限来使用你的模块,以及使用该模块有哪些限制。最简单的,例如你用BSD-3-Clause 或 MIT之类的协议,如下:

{ "license" : "BSD-3-Clause" }

你可以在https://spdx.org/licenses/这个地址查阅协议列表 。

 

和用户相关的属性: author, contributors

"author"是一个码农, "contributors"是一个码农数组。 "person"是一个有一些描述属性的对象,如下 like this:

{ "name" : "Barney Rubble"
, "email" : "b@rubble.com"
, "url" : "http://barnyrubble.tumblr.com/"
}

也可以按如下格式缩写,npm会帮着转换:
"Barney Rubble b@rubble.com (http://barnyrubble.tumblr.com/)"
email和url属性实际上都是可以省略的。描述用户信息的还有一个"maintainers"(维护者)属性。

 

files

"files"属性的值是一个数组,内容是模块下文件名或者文件夹名,如果是文件夹名,则文件夹下所有的文件也会被包含进来(除非文件被另一些配置排除了)
你也可以在模块根目录下创建一个".npmignore"文件(windows下无法直接创建以"."开头的文件,使用linux命令行工具创建如git bash),写在这个文件里边的文件即便被写在files属性里边也会被排除在外,这个文件的写法".gitignore"类似。

 

main

main属性指定了程序的主入口文件。意思是,如果你的模块被命名为foo,用户安装了这个模块并通过require("foo")来使用这个模块,那么require返回的内容就是main属性指定的文件中 module.exports指向的对象。
它应该指向模块根目录下的一个文件。对大对数模块而言,这个属性更多的是让模块有一个主入口文件,然而很多模块并不写这个属性。

 

bin

很多模块有一个或多个需要配置到PATH路径下的可执行模块,npm让这个工作变得十分简单(实际上npm本身也是通过bin属性安装为一个可执行命令的)
如果要用npm的这个功能,在package.json里边配置一个bin属性。bin属性是一个已命令名称为key,本地文件名称为value的map如下:

{ "bin" : { "myapp" : "./cli.js" } }

 

模块安装的时候,若是全局安装,则npm会为bin中配置的文件在bin目录下创建一个软连接(对于windows系统,默认会在C:UsersusernameAppDataRoamingnpm目录下),若是局部安装,则会在项目内的./node_modules/.bin/目录下创建一个软链接。
因此,按上面的例子,当你安装myapp的时候,npm就会为cli.js在/usr/local/bin/myapp路径创建一个软链接。
如果你的模块只有一个可执行文件,并且它的命令名称和模块名称一样,你可以只写一个字符串来代替上面那种配置,例如:

{ "name": "my-program"
, "version": "1.2.5"
, "bin": "./path/to/program" }

 

作用和如下写法相同:

{ "name": "my-program"
, "version": "1.2.5"
, "bin" : { "my-program" : "./path/to/program" } }

man

制定一个或通过数组制定一些文件来让linux下的man命令查找文档地址。
如果只有一个文件被指定的话,安装后直接使用man+模块名称,而不管man指定的文件的实际名称。例如:

{ "name" : "foo"
, "version" : "1.2.3"
, "description" : "A packaged foo fooer for fooing foos"
, "main" : "foo.js"
, "man" : "./man/doc.1"
}

 

通过man foo命令会得到 ./man/doc.1 文件的内容。
如果man文件名称不是以模块名称开头的,安装的时候会给加上模块名称前缀。因此,下面这段配置:

{ "name" : "foo"
, "version" : "1.2.3"
, "description" : "A packaged foo fooer for fooing foos"
, "main" : "foo.js"
, "man" : [ "./man/foo.1", "./man/bar.1" ]
}

 

会创建一些文件来作为man foo和man foo-bar命令的结果。
man文件必须以数字结尾,或者如果被压缩了,以.gz结尾。数字表示文件将被安装到man的哪个部分。

{ "name" : "foo"
, "version" : "1.2.3"
, "description" : "A packaged foo fooer for fooing foos"
, "main" : "foo.js"
, "man" : [ "./man/foo.1", "./man/foo.2" ]
}

会创建 man foo 和 man 2 foo 两条命令。

 

directories

CommonJs通过directories来制定一些方法来描述模块的结构,看看npm的package.json文件https://registry.npmjs.org/npm/latest ,可以发现里边有这个字段的内容。

目前这个配置没有任何作用,将来可能会整出一些花样来。

 

directories.lib

告诉用户模块中lib目录在哪,这个配置目前没有任何作用,但是对使用模块的人来说是一个很有用的信息。

 

directories.bin

如果你在这里指定了bin目录,这个配置下面的文件会被加入到bin路径下,如果你已经在package.json中配置了bin目录,那么这里的配置将不起任何作用。

 

directories.man

指定一个目录,目录里边都是man文件,这是一种配置man文件的语法糖。

 

directories.doc

在这个目录里边放一些markdown文件,可能最终有一天它们会被友好的展现出来(应该是在npm的网站上)

 

directories.example

放一些示例脚本,或许某一天会有用 - -!

 

repository

指定一个代码存放地址,对想要为你的项目贡献代码的人有帮助。像这样:

"repository" :
  { "type" : "git"
  , "url" : "https://github.com/npm/npm.git"
  }

"repository" :
  { "type" : "svn"
  , "url" : "https://v8.googlecode.com/svn/trunk/"
  }

 

若你的模块放在GitHub, GitHub gist, Bitbucket, or GitLab的仓库里,npm install的时候可以使用缩写标记来完成:

"repository": "npm/npm"

"repository": "gist:11081aaa281"

"repository": "bitbucket:example/repo"

"repository": "gitlab:another/repo"

 

scripts

scripts属性是一个对象,里边指定了项目的生命周期个各个环节需要执行的命令。key是生命周期中的事件,value是要执行的命令。
具体的内容有 install start stop 等,详见https://docs.npmjs.com/misc/scripts

 

config

用来设置一些项目不怎么变化的项目配置,例如port等。
用户用的时候可以使用如下用法:

http.createServer(...).listen(process.env.npm_package_config_port)

 

可以通过npm config set foo:port 80来修改config。详见https://docs.npmjs.com/misc/config

{ "name" : "foo"
, "config" : { "port" : "8080" } }

 

 

dependencies

dependencies属性是一个对象,配置模块依赖的模块列表,key是模块名称,value是版本范围,版本范围是一个字符,可以被一个或多个空格分割。
dependencies也可以被指定为一个git地址或者一个压缩包地址。
不要把测试工具或transpilers写到dependencies中。 下面是一些写法,详见https://docs.npmjs.com/misc/semver

  • version 精确匹配版本
  • >version 必须大于某个版本
  • >=version 大于等于
  • <version 小于
  • <=versionversion 小于
  • ~version "约等于",具体规则详见semver文档
  • ^version "兼容版本"具体规则详见semver文档
  • 1.2.x 仅一点二点几的版本
  • http://... 见下面url作为denpendencies的说明
    • 任何版本
  • "" 空字符,和*相同
  • version1 - version2 相当于 >=version1 <=version2.
  • range1 || range2 范围1和范围2满足任意一个都行
  • git... 见下面git url作为denpendencies的说明
  • user/repo See 见下面GitHub仓库的说明
  • tag 发布的一个特殊的标签,见npm-tag的文档 https://docs.npmjs.com/getting-started/using-tags
  • path/path/path 见下面本地模块的说明
    下面的写法都是可以的:
{ "dependencies" :
  { "foo" : "1.0.0 - 2.9999.9999"
  , "bar" : ">=1.0.2 <2.1.2"
  , "baz" : ">1.0.2 <=2.3.4"
  , "boo" : "2.0.1"
  , "qux" : "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0"
  , "asd" : "http://asdf.com/asdf.tar.gz"
  , "til" : "~1.2"
  , "elf" : "~1.2.3"
  , "two" : "2.x"
  , "thr" : "3.3.x"
  , "lat" : "latest"
  , "dyl" : "file:../dyl"
  }
}

 

 

URLs as Dependencies

在版本范围的地方可以写一个url指向一个压缩包,模块安装的时候会把这个压缩包下载下来安装到模块本地。

 

Git URLs as Dependencies

Git url可以像下面一样:

git://github.com/user/project.git#commit-ish
git+ssh://user@hostname:project.git#commit-ish
git+ssh://user@hostname/project.git#commit-ish
git+http://user@hostname/project/blah.git#commit-ish
git+https://user@hostname/project/blah.git#commit-ish

 

commit-ish 可以是任意标签,哈希值,或者可以检出的分支,默认是master分支。

 

GitHub URLs

支持github的 username/modulename 的写法,#后边可以加后缀写明分支hash或标签:

{
  "name": "foo",
  "version": "0.0.0",
  "dependencies": {
    "express": "visionmedia/express",
    "mocha": "visionmedia/mocha#4727d357ea"
  }
}

 

Local Paths

npm2.0.0版本以上可以提供一个本地路径来安装一个本地的模块,通过npm install xxx --save 来安装,格式如下:

../foo/bar
~/foo/bar
./foo/bar
/foo/bar

 

package.json 生成的相对路径如下:

{
  "name": "baz",
  "dependencies": {
    "bar": "file:../foo/bar"
  }
}

这种属性在离线开发或者测试需要用npm install的情况,又不想自己搞一个npm server的时候有用,但是发布模块到公共仓库时不应该使用这种属性。

 

devDependencies

如果有人想要下载并使用你的模块,也许他们并不希望或需要下载一些你在开发过程中使用的额外的测试或者文档框架。
在这种情况下,最好的方法是把这些依赖添加到devDependencies属性的对象中。
这些模块会在npm link或者npm install的时候被安装,也可以像其他npm配置一样被管理,详见npm的config文档。
对于一些跨平台的构建任务,例如把CoffeeScript编译成JavaScript,就可以通过在package.json的script属性里边配置prepublish脚本来完成这个任务,然后需要依赖的coffee-script模块就写在devDependencies属性种。
例如:

{ "name": "ethopia-waza",
  "description": "a delightfully fruity coffee varietal",
  "version": "1.2.3",
  "devDependencies": {
    "coffee-script": "~1.6.3"
  },
  "scripts": {
    "prepublish": "coffee -o lib/ -c src/waza.coffee"
  },
  "main": "lib/waza.js"
}

 

prepublish脚本会在发布之前运行,因此用户在使用之前就不用再自己去完成编译的过程了。在开发模式下,运行npm install也会执行这个脚本(见npm script文档),因此可以很方便的调试。

 

peerDependencies

有时候做一些插件开发,比如grunt等工具的插件,它们往往是在grunt的某个版本的基础上开发的,而在他们的代码中并不会出现require("grunt")这样的依赖,dependencies配置里边也不会写上grunt的依赖,为了说明此模块只能作为插件跑在宿主的某个版本范围下,可以配置peerDependencies:

{
  "name": "tea-latte",
  "version": "1.3.5",
  "peerDependencies": {
    "tea": "2.x"
  }
}

 

上面这个配置确保再npm install的时候tea-latte会和2.x版本的tea一起安装,而且它们两个的依赖关系是同级的:
├── tea-latte@1.3.5
└── tea@2.2.0
这个配置的目的是让npm知道,如果要使用此插件模块,请确保安装了兼容版本的宿主模块。

 

bundledDependencies

上面的单词少个d,写成bundleDependencies也可以。
指定发布的时候会被一起打包的模块。

 

optionalDependencies

如果一个依赖模块可以被使用, 同时你也希望在该模块找不到或无法获取时npm继续运行,你可以把这个模块依赖放到optionalDependencies配置中。这个配置的写法和dependencies的写法一样,不同的是这里边写的模块安装失败不会导致npm install失败。
当然,这种模块就需要你自己在代码中处理模块确实的情况了,例如:

try {
  var foo = require('foo')
  var fooVersion = require('foo/package.json').version
} catch (er) {
  foo = null
}
if ( notGoodFooVersion(fooVersion) ) {
  foo = null
}

// .. then later in your program ..

if (foo) {
  foo.doFooThings()
}

optionalDependencies 中的配置会覆盖dependencies中的配置,最好只在一个地方写。

 

engines

你可以指定项目运行的node版本范围,如下:

{ "engines" : { "node" : ">=0.10.3 <0.12" } }

 


和dependencies一样,如果你不指定版本范围或者指定为*,任何版本的node都可以。
也可以指定一些npm版本可以正确的安装你的模块,例如:

{ "engines" : { "npm" : "~1.0.20" } }

要注意的是,除非你设置了engine-strict属性,engines属性是仅供参考的。

 

engineStrict

注意:这个属性已经弃用,将在npm 3.0.0 版本干掉。

 

os

可以指定你的模块只能在哪个操作系统上跑:

"os" : [ "darwin", "linux" ]

 


也可以指定黑名单而不是白名单:

"os" : [ "!win32" ]

服务的操作系统是由process.platform来判断的,这个属性允许黑白名单同时存在,虽然没啥必要这样搞...

 

cpu

限制模块只能在某某cpu架构下运行

"cpu" : [ "x64", "ia32" ]


同样可以设置黑名单:

"cpu" : [ "!arm", "!mips" ]

cpu架构通过 process.arch 判断

 

preferGlobal

如果您的软件包主要用于安装到全局的命令行应用程序,那么该值设置为true ,如果它被安装在本地,则提供一个警告。实际上该配置并没有阻止用户把模块安装到本地,只是防止该模块被错误的使用引起一些问题。

 

private

如果这个属性被设置为true,npm将拒绝发布它,这是为了防止一个私有模块被无意间发布出去。如果你只想让模块被发布到一个特定的npm仓库,如一个内部的仓库,可与在下面的publishConfig中配置仓库参数。

 

publishConfig

这个配置是会在模块发布时用到的一些值的集合。如果你不想模块被默认被标记为最新的,或者默认发布到公共仓库,可以在这里配置tag或仓库地址。

 

DEFAULT VALUES

npm设置了一些默认参数,如:

"scripts": {"start": "node server.js"}

如果模块根目录下有一个server.js文件,那么npm start会默认运行这个文件。

"scripts":{"preinstall": "node-gyp rebuild"}

如果模块根目录下有binding.gyp, npm将默认用node-gyp来编译preinstall的脚本

"contributors": [...]

若模块根目录下有AUTHORS 文件,则npm会按Name (url)格式解析每一行的数据添加到contributors中,可以用#添加行注释

 

参考文档列表(https://docs.npmjs.com/)

semver(7)
npm-init(1)
npm-version(1)
npm-config(1)
npm-config(7)
npm-help(1)
npm-faq(7)
npm-install(1)
npm-publish(1)
npm-rm(1)
内容来源于网络如有侵权请私信删除
你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!