vue-cli 脚手架中webpack 配置基础文件详解

需要Word版本 的小伙伴可以发我邮件2445478193@qq.com

一、前言

vue-cli是构建vue单页应用的脚手架,输入一串指定的命令行从而自动生成vue.js+wepack的项目模板。这其中webpack发挥了很大的作用,它使得我们的代码模块化,引入一些插件帮我们完善功能可以将文件打包压缩,图片转base64等。后期对项目的配置使得我们对于脚手架自动生成的代码的理解更为重要,接下来我将基于webpack3.6.0版本结合文档将文件各个击破,纯干料。

重点章节点击查看:package.json;config/index.js;webpack.base.conf.js;webpack.dev.conf.js;webpack.prod.conf.js

二、主体结构

1.   ├─build

2.   ├─config

3.   ├─dist

4.   ├─node_modules

5.   ├─src

6.   │ ├─assets

7.   │ ├─components

8.   │ ├─router

9.   │ ├─App.vue

10.  │ ├─main.js

11.  ├─static

12.  ├─.babelrc

13.  ├─.editorconfig

14.  ├─.gitignore

15.  ├─.postcssrc.js

16.  ├─index.html

17.  ├─package-lock.json

18.  ├─package.json

19.  └─README.md

1、 package.json

项目作为一个大家庭,每个文件都各司其职。package.json来制定名单,需要哪些npm包来参与到项目中来,npm install命令根据这个配置文件增减来管理本地的安装包。

1.   {

2.   //从name到private都是package的配置信息,也就是我们在脚手架搭建中输入的项目描述

3.     "name": "shop",//项目名称:不能以.(点)或者_(下划线)开头,不能包含大写字母,具有明确的的含义与现有项目名字不重复

4.     "version": "1.0.0",//项目版本号:遵循“大版本.次要版本.小版本”

5.     "description": "AVue.js project",//项目描述

6.     "author": "qietuniu",//作者名字

7.     "private": true,//是否私有

8.     //scripts中的子项即是我们在控制台运行的脚本的缩写

9.     "scripts": {

10.     //①webpack-dev-server:启动了http服务器,实现实时编译;

11.     //inline模式会在webpack.config.js入口配置中新增webpack-dev-server/client?http://localhost:8080/的入口,使得我们访问路径为localhost:8080/index.html(相应的还有另外一种模式Iframe);

12.     //progress:显示打包的进度

13.      "dev": "webpack-dev-server --inline --progress --configbuild/webpack.dev.conf.js",  

14.      "start": "npmrun dev",//与npm rundev相同,直接运行开发环境

15.      "build": "nodebuild/build.js"//使用node运行build文件

16.    },

17.    //②dependencies(项目依赖库):在安装时使用--save则写入到dependencies

18.    "dependencies": {

19.      "vue": "^2.5.2",//vue.js

20.      "vue-router": "^3.0.1"//vue的路由插件

21.    },

22.    //和devDependencies(开发依赖库):在安装时使用--save-dev将写入到devDependencies

23.    "devDependencies": {

24.      "autoprefixer": "^7.1.2",//autoprefixer作为postcss插件用来解析CSS补充前缀,例如 display: flex会补充为display:-webkit-box;display:-webkit-flex;display: -ms-flexbox;display: flex。

25.      //babel:以下几个babel开头的都是针对es6解析的插件。用最新标准编写的 JavaScript 代码向下编译成可以在今天随处可用的版本

26.      "babel-core": "^6.22.1",//babel的核心,把 js 代码分析成ast ,方便各个插件分析语法进行相应的处理。

27.      "babel-helper-vue-jsx-merge-props": "^2.0.3",//预制babel-template函数,提供给vue,jsx等使用

28.      "babel-loader": "^7.1.1",//使项目运行使用Babel和webpack来传输js文件,使用babel-core提供的api进行转译

29.      "babel-plugin-syntax-jsx": "^6.18.0",//支持jsx

30.      "babel-plugin-transform-runtime": "^6.22.0",//避免编译输出中的重复,直接编译到build环境中

31.      "babel-plugin-transform-vue-jsx": "^3.5.0",//babel转译过程中使用到的插件,避免重复

32.      "babel-preset-env": "^1.3.2",//转为es5,transform阶段使用到的插件之一

33.      "babel-preset-stage-2": "^6.22.0",//ECMAScript第二阶段的规范

34.      "chalk": "^2.0.1",//用来在命令行输出不同颜色文字

35.      "copy-webpack-plugin": "^4.0.1",//拷贝资源和文件

36.      "css-loader": "^0.28.0",//webpack先用css-loader加载器去解析后缀为css的文件,再使用style-loader生成一个内容为最终解析完的css代码的style标签,放到head标签里

37.      "extract-text-webpack-plugin": "^3.0.0",//将一个以上的包里面的文本提取到单独文件中

38.      "file-loader": "^1.1.4",//③打包压缩文件,与url-loader用法类似

39.      "friendly-errors-webpack-plugin": "^1.6.1",//识别某些类别的WebPACK错误和清理,聚合和优先排序,以提供更好的开发经验

40.      "html-webpack-plugin": "^2.30.1",//简化了HTML文件的创建,引入了外部资源,创建html的入口文件,可通过此项进行多页面的配置

41.      "node-notifier": "^5.1.2",//支持使用node发送跨平台的本地通知

42.      "optimize-css-assets-webpack-plugin": "^3.2.0",//压缩提取出的css,并解决ExtractTextPlugin分离出的js重复问题(多个文件引入同一css文件)

43.      "ora": "^1.2.0",//加载(loading)的插件

44.      "portfinder": "^1.0.13",//查看进程端口

45.      "postcss-import": "^11.0.0",//可以消耗本地文件、节点模块或web_modules

46.      "postcss-loader": "^2.0.8",//用来兼容css的插件

47.      "postcss-url": "^7.2.1",//URL上重新定位、内联或复制

48.      "rimraf": "^2.6.0",//节点的UNIX命令RM—RF,强制删除文件或者目录的命令

49.      "semver": "^5.3.0",//用来对特定的版本号做判断的

50.      "shelljs": "^0.7.6",//使用它来消除shell脚本在UNIX上的依赖性,同时仍然保留其熟悉和强大的命令,即可执行Unix系统命令

51.      "uglifyjs-webpack-plugin": "^1.1.1",//压缩js文件

52.      "url-loader": "^0.5.8",//压缩文件,可将图片转化为base64

53.      "vue-loader": "^13.3.0",//VUE单文件组件的WebPACK加载器

54.      "vue-style-loader": "^3.0.1",//类似于样式加载程序,您可以在CSS加载器之后将其链接,以将CSS动态地注入到文档中作为样式标签

55.      "vue-template-compiler": "^2.5.2",//这个包可以用来预编译VUE模板到渲染函数,以避免运行时编译开销和CSP限制

56.      "webpack": "^3.6.0",//打包工具

57.      "webpack-bundle-analyzer": "^2.9.0",//可视化webpack输出文件的大小

58.      "webpack-dev-server": "^2.9.1",//提供一个提供实时重载的开发服务器

59.      "webpack-merge": "^4.1.0"//它将数组和合并对象创建一个新对象。如果遇到函数,它将执行它们,通过算法运行结果,然后再次将返回的值封装在函数中

60.    },

61.    //engines是引擎,指定node和npm版本

62.    "engines": {

63.      "node": ">= 6.0.0",

64.      "npm": ">= 3.0.0"

65.    },

66.    //限制了浏览器或者客户端需要什么版本才可运行

67.    "browserslist": [

68.      "> 1%",

69.      "last 2 versions",

70.      "not ie <= 8"

71.    ]

72.  }

注释:

§ ①、点这里→webpack运行时的配置文档传送门

§ ②、devDependencies和dependencies的区别: devDependencies里面的插件只用于开发环境,不用于生产环境,即辅助作用,打包的时候需要,打包完成就不需要了。而dependencies是需要发布到生产环境的,自始至终都在。比如wepack等只是在开发中使用的包就写入到devDependencies,而像vue这种项目全程依赖的包要写入到devDependencies。

§ 点这里→更多安装包文档搜索页传送门

§ ③、file-loader和url-loader的区别:以图片为例,file-loader可对图片进行压缩,但是还是通过文件路径进行引入,当http请求增多时会降低页面性能,而url-loader通过设定limit参数,小于limit字节的图片会被转成base64的文件,大于limit字节的将进行图片压缩的操作。总而言之,url-loader是file-loader的上层封装。

§ 点这里→file-loader和 url-loader详解

§ 点这里→file-loader文档传送门

§ 点这里→url-loader文档传送门

2.postcssrc.js

.postcssrc.js文件其实是postcss-loader包的一个配置,在webpack的旧版本可以直接在webpack.config.js中配置,现版本中postcss的文档示例独立出.postcssrc.js,里面写进去需要使用到的插件。

1.   module.exports ={

2.     "plugins": {

3.       "postcss-import": {},//

4.       "postcss-url": {},//

5.       "autoprefixer": {}//

6.     }

7.   }

注释:

§ ①、点这里→postcss-import文档传送门

§ ②、点这里→postcss-url文档传送门

§ ③、点这里→autoprefixer文档传送门

3、 .babelrc

该文件是es6解析的一个配置。

1.   {

2.   //制定转码的规则

3.     "presets": [

4.     //env是使用babel-preset-env插件将js进行转码成es5,并且设置不转码的AMD,COMMONJS的模块文件,制定浏览器的兼容

5.       ["env", {

6.        "modules": false,

7.        "targets": {

8.          "browsers": [">1%", "last 2 versions", "notie <= 8"]

9.        }

10.      }],

11.      "stage-2"

12.    ],

13.   

14.    "plugins": ["transform-vue-jsx", "transform-runtime"]//

15.  }

注释:

§ 点这里→transform-vue-jsx文档传送门

§ 点这里→transform-runtime文档传送门

4、src内文件

我们开发的代码都存放在src目录下,根据需要我们通常会再建一些文件夹。比如pages的文件夹,用来存放页面让components文件夹专门做好组件的工作;api文件夹,来封装请求的参数和方法;store文件夹,使用vuex来作为vue的状态管理工具,我也常叫它作前端的数据库等。

①、assets文件:脚手架自动回放入一个图片在里面作为初始页面的logo。平常我们使用的时候会在里面建立js,css,img,fonts等文件夹,作为静态资源调用

②、components文件夹:用来存放组件,合理地使用组件可以高效地实现复用等功能,从而更好地开发项目。一般情况下比如创建头部组件的时候,我们会新建一个header的文件夹,然后再新建一个header.vue的文件夹

③、router文件夹:该文件夹下有一个叫index.js文件,用于实现页面的路由跳转,具体使用请点击→vue-router传送门

④、App.vue:作为我们的主组件,可通过使用开放入口让其他的页面组件得以显示。

⑤、main.js:作为我们的入口文件,主要作用是初始化vue实例并使用需要的插件,小型项目省略router时可放在该处

注释:具体vue的用法可查看vue官方中文文档传送门

5、其他文件

①、.editorconfig:编辑器的配置文件

②、.gitignore:忽略git提交的一个文件,配置之后提交时将不会加载忽略的文件

③、index.html:页面入口,经过编译之后的代码将插入到这来。

④、package.lock.json:锁定安装时的包的版本号,并且需要上传到git,以保证其他人在npm install时大家的依赖能保证一致

⑤、README.md:可此填写项目介绍

⑥、node_modules:根据package.json安装时候生成的的依赖(安装包)

三、config文件夹

1.   ├─config

2.   │ ├─dev.env.js

3.   │ ├─index.js

4.   │ ├─prod.env.js

1、config/dev.env.js

config内的文件其实是服务于build的,大部分是定义一个变量export出去。

1.   'use strict'//采用严格模式

2.   const merge = require('webpack-merge')//

3.   const prodEnv =require('./prod.env')

4.   //webpack-merge提供了一个合并函数,它将数组和合并对象创建一个新对象。

5.   //如果遇到函数,它将执行它们,通过算法运行结果,然后再次将返回的值封装在函数中.这边将dev和prod进行合并

6.   module.exports =merge(prodEnv, {

7.     NODE_ENV: '"development"'

8.   })

注释:点这里→webpack-merge文档传送门

2、config/prod.env.js

当开发是调取dev.env.js的开发环境配置,发布时调用prod.env.js的生产环境配置。

1.   'use strict'

2.   module.exports ={

3.     NODE_ENV: '"production"'

4.   }

3、config/index.js

1.   'use strict'

2.   const path = require('path')

3.    

4.   module.exports ={

5.     dev: {

6.       // 开发环境下面的配置

7.      assetsSubDirectory: 'static',//子目录,一般存放css,js,image等文件

8.      assetsPublicPath: '/',//根目录

9.      proxyTable: {},//可利用该属性解决跨域的问题

10.      host:'localhost', // 地址

11.      port:8080, //端口号设置,端口号占用出现问题可在此处修改

12.     autoOpenBrowser: false,//是否在编译(输入命令行npm run dev)后打开http://localhost:8080/页面,以前配置为true,近些版本改为false,个人偏向习惯自动打开页面

13.     errorOverlay: true,//浏览器错误提示

14.     notifyOnErrors: true,//跨平台错误提示

15.      poll:false, //使用文件系统(file system)获取文件改动的通知devServer.watchOptions

16.     devtool: 'cheap-module-eval-source-map',//增加调试,该属性为原始源代码(仅限行)不可在生产环境中使用

17.     cacheBusting: true,//使缓存失效

18.     cssSourceMap: true//代码压缩后进行调bug定位将非常困难,于是引入sourcemap记录压缩前后的位置信息记录,当产生错误时直接定位到未压缩前的位置,将大大的方便我们调试

19.    },

20.   

21.    build: {

22.    // 生产环境下面的配置

23.     index: path.resolve(__dirname, '../dist/index.html'),//index编译后生成的位置和名字,根据需要改变后缀,比如index.php

24.     assetsRoot: path.resolve(__dirname, '../dist'),//编译后存放生成环境代码的位置

25.     assetsSubDirectory: 'static',//js,css,images存放文件夹名

26.     assetsPublicPath: '/',//发布的根目录,通常本地打包dist后打开文件会报错,此处修改为./。如果是上线的文件,可根据文件存放位置进行更改路径

27.     productionSourceMap: true,

28.     devtool: '#source-map',//

29.      //unit的gzip命令用来压缩文件,gzip模式下需要压缩的文件的扩展名有js和css

30.     productionGzip: false,

31.     productionGzipExtensions: ['js', 'css'],

32.     bundleAnalyzerReport: process.env.npm_config_report

33.    }

34.  }

注释:点击→devtool文档传送门

四、build文件夹

1.   ├─build

2.   │ ├─build.js

3.   │ ├─check-versions.js

4.   │ ├─utils.js

5.   │ ├─vue-loader.conf.js

6.   │ ├─webpack.base.conf.js

7.   │ ├─webpack.dev.conf.js

8.   │ ├─webpack.prod.conf.js

1、build/build.js

该文件作用,即构建生产版本。package.json中的scripts的build就是node build/build.js,输入命令行npm run build对该文件进行编译生成生产环境的代码。

1.   'use strict'

2.   require('./check-versions')()//check-versions:调用检查版本的文件。加()代表直接调用该函数

3.   process.env.NODE_ENV = 'production'//设置当前是生产环境

4.   //下面定义常量引入插件

5.   const ora = require('ora')//①加载动画

6.   const rm = require('rimraf')//②删除文件

7.   const path = require('path')

8.   const chalk = require('chalk')//③对文案输出的一个彩色设置

9.   const webpack =require('webpack')

10.  const config = require('../config')//默认读取下面的index.js文件

11.  constwebpackConfig = require('./webpack.prod.conf')

12.  //调用start的方法实现加载动画,优化用户体验

13.  const spinner =ora('building for production...')

14.  spinner.start()

15.  //先删除dist文件再生成新文件,因为有时候会使用hash来命名,删除整个文件可避免冗余

16.  rm(path.join(config.build.assetsRoot,config.build.assetsSubDirectory), err => {

17.    if (err) throw err

18.    webpack(webpackConfig, (err, stats)=> {

19.     spinner.stop()

20.      if (err) throw err

21.     process.stdout.write(stats.toString({

22.       colors: true,

23.       modules: false,

24.       children: false, // If youare using ts-loader, setting this to true will make TypeScript errors show upduring build.

25.       chunks: false,

26.       chunkModules: false

27.      }) + '\n\n')

28.   

29.      if(stats.hasErrors()) {

30.       process.exit(1)

31.      }

32.   

33.     console.log(chalk.cyan(' Build complete.\n'))

34.     console.log(chalk.yellow(

35.       '  Tip: built files are meant to beserved over an HTTP server.\n' +

36.       '  Opening index.html over file://won\'t work.\n'

37.      ))

38.    })

39.  })

注释:

§ ①、点这里→ora文档传送门

§ ②、点这里→chalk文档传送门

§ ③、点这里→rimraf文档传送门

2、build/check-version.js

该文件用于检测node和npm的版本,实现版本依赖

1.   'use strict'

2.   const chalk = require('chalk')

3.   const semver = require('semver')//①对版本进行检查

4.   constpackageConfig = require('../package.json')

5.   const shell = require('shelljs')

6.    

7.   functionexec (cmd) {

8.   //返回通过child_process模块的新建子进程,执行 Unix 系统命令后转成没有空格的字符串

9.     returnrequire('child_process').execSync(cmd).toString().trim()

10.  }

11.   

12.  constversionRequirements = [

13.    {

14.      name:'node',

15.     currentVersion: semver.clean(process.version),//使用semver格式化版本

16.     versionRequirement: packageConfig.engines.node//获取package.json中设置的node版本

17.    }

18.  ]

19.   

20.  if(shell.which('npm')) {

21.    versionRequirements.push({

22.      name:'npm',

23.     currentVersion: exec('npm--version'),// 自动调用npm--version命令,并且把参数返回给exec函数,从而获取纯净的版本号

24.     versionRequirement: packageConfig.engines.npm

25.    })

26.  }

27.   

28.  module.exports =function () {

29.    const warnings= []

30.    for (let i = 0; i < versionRequirements.length;i++) {

31.      const mod =versionRequirements[i]

32.   

33.      if(!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {

34.      //上面这个判断就是如果版本号不符合package.json文件中指定的版本号,就执行下面错误提示的代码

35.       warnings.push(mod.name + ': ' +

36.         chalk.red(mod.currentVersion) + ' should be ' +

37.         chalk.green(mod.versionRequirement)

38.       )

39.      }

40.    }

41.   

42.    if(warnings.length) {

43.     console.log('')

44.     console.log(chalk.yellow('To usethis template, you must update following to modules:'))

45.     console.log()

46.   

47.      for (let i = 0; i <warnings.length; i++) {

48.       const warning = warnings[i]

49.       console.log('  ' + warning)

50.      }

51.   

52.     console.log()

53.     process.exit(1)

54.    }

55.  }

注释:

§ 点这里→chalk文档传送门

§ 点这里→semver文档传送门

3、build/utils.js

utils是工具的意思,是一个用来处理css的文件。

1.   'use strict'

2.   const path = require('path')

3.   const config = require('../config')

4.   constExtractTextPlugin = require('extract-text-webpack-plugin')

5.   constpackageConfig = require('../package.json')

6.   //导出文件的位置,根据环境判断开发环境和生产环境,为config文件中index.js文件中定义的build.assetsSubDirectory或dev.assetsSubDirectory

7.   exports.assetsPath = function (_path) {

8.     constassetsSubDirectory = process.env.NODE_ENV === 'production'

9.       ?config.build.assetsSubDirectory

10.      :config.dev.assetsSubDirectory

11.  //Node.js path 模块提供了一些用于处理文件路径的小工具①

12.    returnpath.posix.join(assetsSubDirectory, _path)

13.  }

14.   

15.  exports.cssLoaders = function (options){

16.    options =options || {}

17.  //使用了css-loader和postcssLoader,通过options.usePostCSS属性来判断是否使用postcssLoader中压缩等方法

18.    const cssLoader= {

19.     loader: 'css-loader',

20.     options: {

21.       sourceMap: options.sourceMap

22.      }

23.    }

24.   

25.    constpostcssLoader = {

26.     loader: 'postcss-loader',

27.     options: {

28.       sourceMap: options.sourceMap

29.      }

30.    }

31.    functiongenerateLoaders (loader, loaderOptions) {

32.      const loaders =options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]

33.      if (loader){

34.       loaders.push({

35.         loader: loader + '-loader',

36.         //Object.assign是es6语法的浅复制,后两者合并后复制完成赋值

37.         options: Object.assign({}, loaderOptions, {

38.           sourceMap: options.sourceMap

39.         })

40.       })

41.      }

42.   

43.      if(options.extract) {

44.      //ExtractTextPlugin可提取出文本,代表首先使用上面处理的loaders,当未能正确引入时使用vue-style-loader

45.       returnExtractTextPlugin.extract({

46.         use: loaders,

47.         fallback: 'vue-style-loader'

48.       })

49.      } else {

50.      //返回vue-style-loader连接loaders的最终值

51.       return ['vue-style-loader'].concat(loaders)

52.      }

53.    }

54.    return {

55.      css:generateLoaders(),//需要css-loader 和vue-style-loader

56.     postcss: generateLoaders(),//需要css-loader和postcssLoader 和 vue-style-loader

57.      less:generateLoaders('less'),//需要less-loader 和vue-style-loader

58.      sass:generateLoaders('sass', { indentedSyntax: true }),//需要sass-loader和 vue-style-loader

59.      scss:generateLoaders('sass'),//需要sass-loader 和vue-style-loader

60.     stylus: generateLoaders('stylus'),//需要stylus-loader 和 vue-style-loader

61.      styl:generateLoaders('stylus')//需要stylus-loader 和 vue-style-loader

62.    }

63.  }

64.  exports.styleLoaders = function (options){

65.    const output =[]

66.    const loaders =exports.cssLoaders(options)

67.      //将各种css,less,sass等综合在一起得出结果输出output

68.    for (const extensionin loaders){

69.      const loader =loaders[extension]

70.     output.push({

71.       test: newRegExp('\\.' + extension + '$'),

72.       use: loader

73.      })

74.    }

75.   

76.    return output

77.  }

78.   

79.  exports.createNotifierCallback = () => {

80.  //发送跨平台通知系统

81.    const notifier= require('node-notifier')

82.   

83.    return(severity, errors) => {

84.      if (severity!== 'error') return

85.  //当报错时输出错误信息的标题,错误信息详情,副标题以及图标

86.      const error =errors[0]

87.      const filename= error.file && error.file.split('!').pop()

88.   

89.     notifier.notify({

90.       title: packageConfig.name,

91.       message: severity + ': ' + error.name,

92.       subtitle: filename || '',

93.       icon: path.join(__dirname, 'logo.png')

94.      })

95.    }

96.  }

注释:

§ path.posix:提供对路径方法的POSIX(可移植性操作系统接口)特定实现的访问,即可跨平台,区别于win32。

§ path.join:用于连接路径,会正确使用当前系统的路径分隔符,Unix系统是"/",Windows系统是"\"

§ 点击→path用法传送门

4、vue-loader.conf.js

该文件的主要作用就是处理.vue文件,解析这个文件中的每个语言块(template、script、style),转换成js可用的js模块。

1.   'use strict'

2.   const utils = require('./utils')

3.   const config = require('../config')

4.   constisProduction = process.env.NODE_ENV === 'production'

5.   constsourceMapEnabled = isProduction

6.     ?config.build.productionSourceMap

7.     :config.dev.cssSourceMap

8.   //处理项目中的css文件,生产环境和测试环境默认是打开sourceMap,而extract中的提取样式到单独文件只有在生产环境中才需要

9.   module.exports ={

10.    loaders:utils.cssLoaders({

11.     sourceMap: sourceMapEnabled,

12.     extract: isProduction

13.    }),

14.    cssSourceMap: sourceMapEnabled,

15.    cacheBusting: config.dev.cacheBusting,

16.     // 在模版编译过程中,编译器可以将某些属性,如 src 路径,转换为require调用,以便目标资源可以由 webpack 处理.

17.    transformToRequire: {

18.     video: ['src', 'poster'],

19.     source: 'src',

20.      img: 'src',

21.     image: 'xlink:href'

22.    }

23.  }

5、webpack.base.conf.js

webpack.base.conf.js是开发和生产共同使用提出来的基础配置文件,主要实现配制入口,配置输出环境,配置模块resolve和插件等。

1.   'use strict'

2.   const path = require('path')

3.   const utils = require('./utils')

4.   const config = require('../config')

5.   constvueLoaderConfig = require('./vue-loader.conf')

6.    

7.   function resolve(dir) {

8.   //拼接出绝对路径

9.     returnpath.join(__dirname, '..', dir)

10.  }

11.  module.exports ={

12.  //path.join将路径片段进行拼接,而path.resolve将以/开始的路径片段作为根目录,在此之前的路径将会被丢弃

13.  //path.join('/a', '/b') // 'a/b',path.resolve('/a', '/b')// '/b'

14.    context:path.resolve(__dirname, '../'),

15.    //配置入口,默认为单页面所以只有app一个入口

16.    entry: {

17.      app: './src/main.js'

18.    },

19.    //配置出口,默认是/dist作为目标文件夹的路径

20.    output: {

21.      path:config.build.assetsRoot,//路径

22.     filename: '[name].js',//文件名

23.     publicPath: process.env.NODE_ENV === 'production'

24.       ? config.build.assetsPublicPath

25.       : config.dev.assetsPublicPath//公共存放路径

26.    },

27.    resolve: {

28.    //自动的扩展后缀,比如一个js文件,则引用时书写可不要写.js

29.     extensions: ['.js', '.vue', '.json'],

30.      //创建路径的别名,比如增加'components': resolve('src/components')

31.      alias: {

32.       'vue$': 'vue/dist/vue.esm.js',

33.       '@': resolve('src'),

34.      }

35.    },

36.    //使用插件配置相应文件的处理方法

37.    module: {

38.     rules: [

39.      //使用vue-loader将vue文件转化成js的模块①

40.       {

41.         test: /\.vue$/,

42.         loader: 'vue-loader',

43.         options: vueLoaderConfig

44.       },

45.       //js文件需要通过babel-loader进行编译成es5文件以及压缩等操作②

46.       {

47.         test: /\.js$/,

48.         loader: 'babel-loader',

49.         include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]

50.       },

51.       //图片、音像、字体都使用url-loader进行处理,超过10000会编译成base64③

52.       {

53.         test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,

54.         loader: 'url-loader',

55.         options: {

56.           limit: 10000,

57.           name: utils.assetsPath('img/[name].[hash:7].[ext]')

58.         }

59.       },

60.       {

61.         test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,

62.         loader: 'url-loader',

63.         options: {

64.           limit: 10000,

65.           name: utils.assetsPath('media/[name].[hash:7].[ext]')

66.         }

67.       },

68.       {

69.         test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,

70.         loader: 'url-loader',

71.         options: {

72.           limit: 10000,

73.           name: utils.assetsPath('fonts/[name].[hash:7].[ext]')

74.         }

75.       }

76.      ]

77.    },

78.    //以下选项是Node.js全局变量或模块,这里主要是防止webpack注入一些Node.js的东西到vue中

79.    node:

80.     setImmediate: false,

81.     dgram: 'empty',

82.      fs: 'empty',

83.      net: 'empty',

84.      tls: 'empty',

85.     child_process: 'empty'

86.    }

87.  }

注释:

§ ①、点击→vue-loader文档传送门

§ ②、点击→babel-loader文档传送门

6、webpack.dev.conf.js

1.   'use strict'

2.   const utils = require('./utils')

3.   const webpack =require('webpack')

4.   const config = require('../config')

5.   //通过webpack-merge实现webpack.dev.conf.js对wepack.base.config.js的继承

6.   const merge = require('webpack-merge')

7.   const path = require('path')

8.   constbaseWebpackConfig = require('./webpack.base.conf')

9.   constCopyWebpackPlugin = require('copy-webpack-plugin')

10.  constHtmlWebpackPlugin = require('html-webpack-plugin')

11.  //美化webpack的错误信息和日志的插件①

12.  constFriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')

13.  constportfinder = require('portfinder')// 查看空闲端口位置,默认情况下搜索8000这个端口②

14.  const HOST =process.env.HOST//③processs为node的一个全局对象获取当前程序的环境变量,即host

15.  const PORT =process.env.PORT && Number(process.env.PORT)

16.   

17.  constdevWebpackConfig = merge(baseWebpackConfig, {

18.    module: {

19.    //规则是工具utils中处理出来的styleLoaders,生成了css,less,postcss等规则

20.     rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap,usePostCSS: true })

21.    },

22.   

23.    devtool:config.dev.devtool,  //增强调试,上文有提及

24.    //此处的配置都是在config的index.js中设定好了

25.    devServer: {//

26.     clientLogLevel: 'warning',//控制台显示的选项有none, error, warning 或者 info

27.      //当使用HTML5 History API 时,任意的 404 响应都可能需要被替代为 index.html

28.     historyApiFallback: {

29.       rewrites: [

30.         { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },

31.       ],

32.      },

33.      hot: true,//热加载

34.     contentBase: false,

35.     compress: true,//压缩

36.      host:HOST || config.dev.host,

37.      port:PORT || config.dev.port,

38.      open:config.dev.autoOpenBrowser,//调试时自动打开浏览器

39.     overlay: config.dev.errorOverlay

40.       ? { warnings: false, errors: true }

41.       : false,// warning和 error 都要显示

42.     publicPath: config.dev.assetsPublicPath,

43.     proxy: config.dev.proxyTable,//接口代理

44.     quiet: true, //控制台是否禁止打印警告和错误,若用FriendlyErrorsPlugin 此处为 true

45.     watchOptions: {

46.       poll: config.dev.poll, 文件系统检测改动

47.      }

48.    },

49.    plugins: [

50.      new webpack.DefinePlugin({

51.       'process.env': require('../config/dev.env')

52.      }),

53.      new webpack.HotModuleReplacementPlugin(),//⑤模块热替换插件,修改模块时不需要刷新页面

54.      new webpack.NamedModulesPlugin(), // 显示文件的正确名字

55.      new webpack.NoEmitOnErrorsPlugin(),//当webpack编译错误的时候,来中端打包进程,防止错误代码打包到文件中

56.      // https://github.com/ampedandwired/html-webpack-plugin

57.      // 该插件可自动生成一个 html5 文件或使用模板文件将编译好的代码注入进去⑥

58.      newHtmlWebpackPlugin({

59.       filename: 'index.html',

60.       template: 'index.html',

61.       inject: true

62.      }),

63.      newCopyWebpackPlugin([//复制插件

64.       {

65.         from: path.resolve(__dirname, '../static'),

66.         to: config.dev.assetsSubDirectory,

67.         ignore: ['.*']//忽略.*的文件

68.       }

69.      ])

70.    ]

71.  })

72.   

73.  module.exports =newPromise((resolve,reject) => {

74.    portfinder.basePort = process.env.PORT|| config.dev.port

75.    //查找端口号

76.    portfinder.getPort((err, port) => {

77.      if (err) {

78.       reject(err)

79.      } else {

80.      //端口被占用时就重新设置evn和devServer的端口

81.       process.env.PORT = port

82.       devWebpackConfig.devServer.port = port

83.       //友好地输出信息

84.       devWebpackConfig.plugins.push(newFriendlyErrorsPlugin({

85.         compilationSuccessInfo: {

86.           messages: [`Yourapplication is running here: http://${devWebpackConfig.devServer.host}:${port}`],

87.         },

88.         onErrors: config.dev.notifyOnErrors

89.         ? utils.createNotifierCallback()

90.         : undefined

91.       }))

92.       resolve(devWebpackConfig)

93.      }

94.    })

95.  })

注释:

§ ①、点击→friendly-errors-webpack-plugin文档传送门

§ ②、点击→process文档传送门

§ ③、点击→babel-loader文档传送门

§ ④、点击→devtool文档传送门

§ ⑤、点击→webpack的HotModuleReplacementPlugin文档传送门

§ ⑥、点击→html-webpack-plugin文档传送门

7、webpack.prod.conf.js

1.   'use strict'

2.   const path = require('path')

3.   const utils = require('./utils')

4.   const webpack =require('webpack')

5.   const config = require('../config')

6.   const merge = require('webpack-merge')

7.   constbaseWebpackConfig = require('./webpack.base.conf')

8.   constCopyWebpackPlugin = require('copy-webpack-plugin')

9.   constHtmlWebpackPlugin = require('html-webpack-plugin')

10.  constExtractTextPlugin = require('extract-text-webpack-plugin')

11.  constOptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')

12.  constUglifyJsPlugin = require('uglifyjs-webpack-plugin')

13.   

14.  const env = require('../config/prod.env')

15.   

16.  constwebpackConfig = merge(baseWebpackConfig, {

17.    module: {

18.    //调用utils.styleLoaders的方法

19.     rules: utils.styleLoaders({

20.       sourceMap: config.build.productionSourceMap,//开启调试的模式。默认为true

21.       extract: true,

22.       usePostCSS: true

23.      })

24.    },

25.    devtool:config.build.productionSourceMap ? config.build.devtool : false,

26.    output: {

27.      path:config.build.assetsRoot,

28.     filename: utils.assetsPath('js/[name].[chunkhash].js'),

29.     chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')

30.    },

31.    plugins: [

32.      new webpack.DefinePlugin({

33.       'process.env': env

34.      }),

35.      newUglifyJsPlugin({

36.       uglifyOptions: {

37.         compress: {//压缩

38.           warnings: false//警告:true保留警告,false不保留

39.         }

40.       },

41.       sourceMap: config.build.productionSourceMap,

42.       parallel: true

43.      }),

44.      newExtractTextPlugin({//抽取文本。比如打包之后的index页面有style插入,就是这个插件抽取出来的,减少请求

45.       filename: utils.assetsPath('css/[name].[contenthash].css'),  

46.       allChunks: true,

47.      }),

48.   

49.      newOptimizeCSSPlugin({//优化css的插件

50.       cssProcessorOptions: config.build.productionSourceMap

51.         ? { safe: true, map: { inline: false } }

52.         : { safe: true }

53.      }),

54.   

55.      newHtmlWebpackPlugin({//html打包

56.       filename: config.build.index,

57.       template: 'index.html',

58.       inject: true,

59.       minify: {//压缩

60.         removeComments: true,//删除注释

61.         collapseWhitespace: true,//删除空格

62.         removeAttributeQuotes: true//删除属性的引号 

63.       },

64.   

65.       chunksSortMode: 'dependency'//模块排序,按照我们需要的顺序排序

66.      }),

67.   

68.      new webpack.HashedModuleIdsPlugin(),

69.      newwebpack.optimize.ModuleConcatenationPlugin(),

70.      newwebpack.optimize.CommonsChunkPlugin({//抽取公共的模块

71.       name: 'vendor',

72.       minChunks (module) {  

73.         return (

74.           module.resource &&

75.           /\.js$/.test(module.resource) &&

76.           module.resource.indexOf(

77.             path.join(__dirname, '../node_modules')

78.           ) === 0

79.         )

80.       }

81.      }),

82.      newwebpack.optimize.CommonsChunkPlugin({

83.       name: 'manifest',

84.       minChunks: Infinity

85.      }),

86.      newwebpack.optimize.CommonsChunkPlugin({

87.       name: 'app',

88.       async: 'vendor-async',

89.       children: true,

90.       minChunks: 3

91.      }),

92.      newCopyWebpackPlugin([//复制,比如打包完之后需要把打包的文件复制到dist里面

93.       {

94.         from: path.resolve(__dirname, '../static'),

95.         to: config.build.assetsSubDirectory,

96.         ignore: ['.*']

97.       }

98.      ])

99.    ]

100. })

101.  

102. if(config.build.productionGzip) {

103.   constCompressionWebpackPlugin = require('compression-webpack-plugin')

104.  

105.   webpackConfig.plugins.push(

106.     newCompressionWebpackPlugin({

107.      asset: '[path].gz[query]',

108.      algorithm: 'gzip',

109.      test: newRegExp(

110.        '\\.(' +

111.        config.build.productionGzipExtensions.join('|') +

112.        ')$'

113.      ),

114.      threshold: 10240,

115.      minRatio: 0.8

116.     })

117.   )

118. }

119.  

120. if(config.build.bundleAnalyzerReport) {

121.   constBundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

122.   webpackConfig.plugins.push(newBundleAnalyzerPlugin())

123. }

124.  

125. module.exports =webpackConfig

 

1.Vue.js是什么?

Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。另一方面,Vue 完全有能力驱动采用单文件组件和 Vue 生态系统支持的库开发的复杂单页应用。

Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

2.Vue.js到底是什么?

想必现在能看到我这篇文章的人,都是用着APP或者网页版知乎在阅读把。Vue.js就是一个用于搭建类似于网页版知乎这种表单项繁多,且内容需要根据用户的操作进行修改的网页版应用

5.Vue.js为什么能让基于网页的前端应用程序开发起来这么方便?

因为Vue.js有声明式,响应式的数据绑定,与组件化的开发,并且还使用了Virtual DOM这个看名字就觉得高大上的技术。

可是这些名词都是啥?

6.响应式的数据绑定

这里的响应式不是@media 媒体查询中的响应式布局,而是指vue.js会自动对页面中某些数据的变化做出响应。至于是如何响应的,大家可以先把下面这段代码随便粘贴到一个扩展名为html的文件然后用浏览器打开,随便在文本框里面输入一些文字,观察一下页面变化。

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>vue.js测试 - 代码之美专栏</title>
	<!-- author:昌维 代码之美 https://zhuanlan.zhihu.com/codes -->
	<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
	<div id="app">
		<input type="text" name="" value="" placeholder="在这里输入文字,下面会跟着变化" v-model="message">
		<hr>
		<p>{{ message }}</p>
	</div>
	<script type="text/javascript">
		var app = new Vue({
		  el: '#app',
		  data: {
		    message: 'Hello Vue!'
		  }
		})
	</script>
</body>
</html>

是不是会发现一个很神奇的现象,文本框里面输入的文字和后面的p标签中的内容一起变化?

换句话说,p标签里面通过{{ message }}这个写法与input标签中的value绑定在了一起,其中变化,另外一个和它绑定的数据就跟着变化。

结合标题来说,就是vue.js会自动响应数据的变化情况,并且根据用户在代码中预先写好的绑定关系,对所有绑定在一起的数据和视图内容都进行修改。而这种绑定关系,在图上是以input 标签的v-model属性来声明的,因此你在别的地方可能也会看到有人粗略的称vue.js为声明式渲染的模版引擎。

7.组件化开发

还记得在传统前端开发的时候,我们都是每个人做一个页面,然后最后套入各种后端模版引擎,比如说PHP的Smarty或者Java的JSP等等。

但是现在我们做单页应用,页面交互和结构十分复杂,一个页面上就有许许多多的模块需要编写,而且往往一个模块的代码量和工作量就非常庞大,如果还按照原先的方法来开发,那么会累死人。而且遇到以后的产品需求变更,修改起来也非常麻烦,生怕动了其中一个div之后,其他div跟着雪崩,整个页面全部乱套,或者由于JavaScript的事件冒泡机制,导致修改一些内层的DOM事件处理函数之后,出现各种莫名其妙的诡异BUG。

在面向对象编程中,我们可以使用面向对象的思想将各种模块打包成类或者把一个大的业务模块拆分成更多更小的几个类。在面向过程编程中,我们也可以把一些大功能拆分成许多函数,然后分配给不同的人来开发。

在前端应用,我们是否也可以像编程一样把模块封装呢?这就引入了组件化开发的思想。

Vue.js通过组件,把一个单页应用中的各种模块拆分到一个一个单独的组件(component)中,我们只要先在父级应用中写好各种组件标签(占坑),并且在组件标签中写好要传入组件的参数(就像给函数传入参数一样,这个参数叫做组件的属性),然后再分别写好各种组件的实现(填坑),然后整个应用就算做完了。

8.Virtual DOM

现在的网速越来越快了,很多人家里都是几十甚至上百M的光纤,手机也是4G起步了,按道理一个网页才几百K,而且浏览器本身还会缓存很多资源文件,那么几十M的光纤为什么打开一个之前已经打开过,已经有缓存的页面还是感觉很慢呢?这就是因为浏览器本身处理DOM也是有性能瓶颈的,尤其是在传统开发中,用JQuery或者原生的JavaScript DOM操作函数对DOM进行频繁操作的时候,浏览器要不停的渲染新的DOM树,导致页面看起来非常卡顿。

而Virtual DOM则是虚拟DOM的英文,简单来说,他就是一种可以预先通过JavaScript进行各种计算,把最终的DOM操作计算出来并优化,由于这个DOM操作属于预处理操作,并没有真实的操作DOM,所以叫做虚拟DOM。最后在计算完毕才真正将DOM操作提交,将DOM操作变化反映到DOM树上。

对于vue.js的Virtual DOM,目前业界有着褒贬不一的评价。有人认为Vue.js作为一个轻量级框架,引入Virtual DOM会加大Vue.js本身的代码尺寸,也会消耗更多CPU(手机上会更耗电)(注意:消耗更多的CPU并不意味着会更卡,因为JavaScript计算是后台计算,他的计算量还不至于让DOM操作变得卡顿),并且在操作单个DOM元素的时候,反而多了一道计算工序,会更慢。但也有人认为基本上会用Vue.js开发的都是页面中内容很多的元素,肯定操作的DOM量级普遍较大,平均一下还是比较划算的。

9.我到底该怎么用Vue.js做单页应用开发?

说了这么多,我还是不知道怎么用它做出一个像知乎那样的页面啊,到底怎么学它呢?

前面我们看了一个响应式的数据绑定案例,那只是一个DEMO,而且也看不出有什么实际意义,离真正的单页应用程序还差得远,到底怎么用它开发真实的项目呢?

我的建议是,先把介绍 - vue.js官方文档的基础部分硬着头皮看一遍。除了组件这个小节涉及到了很多晦涩难懂的名词以外,前面几章完全就是把Vue.js当作一个模版引擎来用。

然后开始学习ECMAScript6,Webpack,NPM以及Vue-Cli的基本用法,最好对Node.js也要有所了解。

最后组件部分先大致看一遍,了解组件里面都有哪些概念之后,开始看网上各种实战视频以及文章还有别人开源的源代码。

11.我在学习Vue.js的时候老是听到Webpack,这是啥?

Webpack是一个前端打包和构建工具。如果你之前一直是手写HTML,CSS,JavaScript,并且通过link标签将CSS引入你的HTML文件,以及通过Script标签的src属性引入外部的JS脚本,那么你肯定会对这个工具感到陌生。不要紧,我们先来了解一下为什么要用Webpack,然后带着原因去学习就好了。

12.为什么要用Webpack

前面说了,做一个单页应用程序本身就相当复杂,而且在做的时候肯定会使用到很多素材和别的第三方库,我们该如何去管理这些东西呢?

还有前面讲到了Webpack是一个前端打包工具,前端代码为什么要打包呢?因为单页应用程序中用到很多素材,如果每一个素材都通过在HTML中以src属性或者link来引入,那么请求一个页面的时候,可能浏览器就要发起十多次请求,往往请求的这些资源都是一些脚本代码或者很小的图片,这些资源本身才几k,下载连1秒都不需要,但是由于HTTP是应用层协议,它的下层是TCP这个运输层协议,TCP的握手和挥手过程消耗的时间可能比下载资源本身还要长,所以需要把这些小文件全部打包成一个文件,这样只要一次TCP握手和挥手的过程,就把多个资源给下载下来了,并且多个资源由于都是共享一个HTTP请求,所以head等部分也是共享的,相当于形成了规模效应,让网页展现更快,用户体验更好。

前面说到Webpack还有构建的功能,这就不得不提到了ECMAScript6这个新版本的JavaScript,但是现在国内外还有很多人用着老版本的浏览器,这些浏览器并不支持ECMAScript6,那么我们的前端项目如何在这种浏览器上运行呢?这就需要Webpack的Loader自动载入一个转换器来将我们写的ECMAScript6转换成浏览器能支持的老版本JavaScript语言,这个转换器的名字叫做babel,如果你以后听到或者看到了这个单词,应该要知道它就是一个ECMAScript6 to 老版本JavaScript的转换器了。这也是Webpack的构建功能。当然对前端有更深入的同学还会知道Sass,Less,stylus之类的CSS预处理器,我们也可以通过在Loader中编写特定的规则来实现自动将这些CSS预处理语言转换成普通浏览器能识别的CSS代码。

开头的介绍提到了vue.js可以使用单文件组件开发项目,其实也是通过Webpack将单文件组件中的模版,样式以及JS转换到主页面中

当然Webpack不止这点功能,它还可以通过安装各种插件来扩展,比如说热加载技术,就是解放键盘的F5键。让我们修改代码,并且按Ctrl+S保存之后,浏览器页面自动刷新变化,不需要我们去手动刷新,还有一些插件可以自动添加注释,自动给CSS代码加上一些浏览器内核对CSS3兼容前缀,就像webkit-xxx之类的一样。

13.NPM和Node.js又是什么?它们是什么关系?

首先讲讲Node.js。我们知道通常情况下,JavaScript的运行环境都是浏览器,因此JavaScript的能力也就局限于浏览器能赋予它的权限了。比如说读写本地系统文件这种操作,一般情况下运行在浏览器中的JavaScript代码是没有这个操作权限的。如果我们想用JavaScript写出一些能够运行在操作系统上的,能够具有像PHP,JAVA之类的编程语言具有的功能的程序该怎么办呢?Node.js就解决了这个问题。Node.js是一个服务端的JavaScript运行环境,通过Node.js可以实现用JavaScript写独立程序。像我们之前提到的Webpack就是Node.js写的,所以作为一个前端开发,即使你不用Node.js写独立程序,也得配一个Node.js运行环境,毕竟很多前端工具都是使用它写的。

NPM是一个node.js的包管理器。我们在传统开发的时候,JQuery.js大多都是百度搜索,然后去官网下载,或者直接引入CDN资源,这种方法太过于麻烦。如果以后遇到其他的包,这个包的代码本身可能还调用了其他的包(也称这个包和其他的那几个包存在依赖关系),那么我们要在自己的项目中引入一个包将变得十分困难。现在我们有了NPM这个包管理器,直接可以通过

npm install xxx包名称

的方式引入它,比如说

npm install vue

就自动在当前项目文件夹下导入了这个包,并且npm自动下载好了vue这个包依赖的其他包。

至于有的人在按照网上的npm教程配置的时候踩坑了,发现下载速度很慢或者完全下载不了,那是因为我国有着众所周知的原因,网上也有各种解决方法可以解决这个问题,大家多善用搜索引擎。

前面提到了Webpack可以安装各种插件来扩展功能,其实也是通过这种方式扩展。

如果你学过PHP的话,NPM就和PHP里面的Composer差不多。也和CentOS下的yum和Ubuntu下的apt-get差不多。

14.Vue-CLi又是啥?

它是一个vue.js的脚手架工具。说白了就是一个自动帮你生成好项目目录,配置好Webpack,以及各种依赖包的工具,它可以通过

npm install vue-cli -g

的方式安装,后面的-g表示全局安装的意思,意味着你可以打开命令行之后直接通过vue

17.我在很多地方还看到Vuex和Vue-route,它们又是什么?

Vuex是vue的一个状态管理器。用于集中管理一个单页应用程序中的各种状态。

Vue-route是vue的一个前端路由器,这个路由器不是我们上网用的路由器,而是一个管理请求入口和页面映射关系的东西。它可以实现对页面局部进行无刷新的替换,让用户感觉就像切换到了网页一样。

要讲清楚这两个东西,又得花费大量篇幅,所以这里只是简单提一下,先学好vue.js本身才是最重要的。

18.我还在一些地方看到过Vue-resource和Axios,它们又是什么?

我们在传统的前后端不分离的开发中,后端直接把数据通过模版引擎拼接进了返回的HTML中。而现在做单页应用程序属于前后端分离开发,那么这个单页应用程序中的数据就得通过ajax的方式获取,也要通过ajax的方式提交到后端。

在传统开发中我们都是通过xmlhttprequest手动操作,或者通过JQuery的ajax方法来进行数据提交获取。

vue.js本身没有封装ajax操作库,所以我们要通过Vue-resource和Axios来进行ajax操作,而因为种种原因,现在vue.js2.0已经将axios作为官方推荐的ajax库了。



Logo

前往低代码交流专区

更多推荐