Skip to content

HeadmasterTan/first-vue-project

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

转自:[造轮子教程]十分钟搭建Webpack+Vue项目

原文使用的版本比较老,且看且珍惜。

之前说到的vue.js的安装--vue-cli脚手架采用的vue-cli脚手架进行安装的自动构建的项目,但是实际项目中可能会有特殊的要求,所以没有使用到脚手架

推荐vue项目目录结构:

|-- ./config  // 全局变量
|-- ./dist    // 编译后的项目代码
|-- ./src     // 项目源码
    |-- apis        // api封装
    |-- components  // Vue组件
    |-- lib         // js工具类
    |-- router      // 路由
    |-- store       // Vuex的store
        |-- modules     // Vuex模块
    |-- style       // css
    |-- views       // 页面组件
    |-- index.js    // 入口文件
|-- ./webpack.config    // Webpack各种环境的配置文件
|-- ./package.json

首先安装Node.js,选择需要的版本自行安装,这里不多赘述。

初始化项目

为了方便阅读,这里的项目目录结构使用的是上面的推荐目录。

在项目的根目录处(此处为./)使用使用命令行执行npm init -y-y的作用是跳过信息填写)创建package.json

入口文件

1、在根目录下直接建立一个index.html,作为页面的入口文件。

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>myProject</title>
  </head>
  <body>
    <div id="app">{{ message }}</div> <!-- Vue模板入口 -->
    <script src="dist/index.js" charset="utf-8"></script>
  </body>
</html>

2、在src下建立一个index.js,作为Vue的入口文件

// import...from的语法是ES6的,需要用到babel,后面再说
// require的语法是Commonjs的,webpack已经实现了,可以直接使用
const Vue = require('vue')
new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue.js!'
  }
})

3、安装模块

安装Vue:npm install --save vue

安装Webpack:npm install --save-dev webpack

4、使用webpack编译打包

除非在全局安装webpack,使用本地安装的webpack你需要写一长串的命令.\node_modules\.bin\webpack src\index.js dist\index.js,所以建议在package.jsonscript加入运行脚本,添加之后package.json如下:

{
  "name": "myProject",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack src/index.js dist/index.js"  // <---添加这句
  },
  "keywords": [],
  "author": "Headmaster_Tan",
  "license": "ISC",
  "dependencies": {
    "vue": "^2.4.2"
  },
  "devDependencies": {
    "webpack": "^3.5.5"
  }
}

然后你可以执行npm run dev命令进行打包,此时打开index.html,如无意外应该是会报错的。这是一个vue@1 -> vue@2的版本升级问题,报错内容如下:

[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

解决办法是使用下面的webpack配置文件对'vue'进行定义,报错原因详情可以看这里:Vue 2.0 升(cai)级(keng)之旅

编写webpack配置文件

上一步中直接使用webpack运行脚本webpack [入口文件] [出口文件],显然对于后期添加webpack插件和不同环境的配置是不行的。

1、在项目根目录下创建webpack.config文件夹专门用于存放webpack的配置文件(当然也是完全可以只建一个webpack.config.js文件的)

2、为了让配置文件不同的编译环境能够复用(例如loaders的配置,不管在开发环境还是生产环境肯定都是一样的),在webpack.config中首先创建一个base.js

const path = require('path')
const root = path.resolve(__dirname, '..')  // 项目的根目录绝对路径

module.exports = {
  entry: path.join(root, 'src/index.js'),    // 入口文件路径
  output: {
    path: path.join(root, 'dist'),    // 出口目录
    filename: 'index.js'   // 出口文件名
  }
}

上面这段配置就实现了webpack src/index.js dist/index.js的功能,还可以额外拓展一下,变成:

const path = require('path')
const root = path.resolve(__dirname, '..')    // 项目的根目录绝对路径

module.exports = {
  entry: path.join(root, 'src/main.js'),    // 入口文件路径
  output: {
    path: path.join(root, 'dist'),    // 出口目录
    filename: 'main.js'   // 出口文件名
  },
  resolve: {
    alias: {    // 配置目录别名
      // 在任意目录下require('components/example') 相当于require('项目根目录/src/components/example')
      components: path.join(root, 'src/components'),
      views: path.join(root, 'src/views'),
      styles: path.join(root, 'src/styles'),
      store: path.join(root, 'src/store'),
      'vue': 'vue/dist/vue.js'    // 这里就是解决上面vue@1 -> vue@2版本升级的报错问题
    },
    // 这里注意在新版本的webpack中,extensions是不能包含''空串的
    extensions: ['.js', '.vue'],    // 引用js和vue文件可以省略后缀名
  },
  module: {   // 配置loader
    // 在这里 -loader 不能忽略,乖乖加上吧
    loaders: [
      {test: /\.vue$/, loader: 'vue-loader'},    // 所有.vue结尾的文件,使用vue-loader
      {test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/}   // .js文件使用babel-loader,切记排除node_modules目录
    ]
  }
}

babel-loader就是将ES6的语法解析为ES5,要用到babel则根目录下要新建.babelrc文件用于配置babel

{
  "presets": ["es2015"]
}

使用了vue-loaderbabel-loader需要安装包:

npm install --save-dev vue-loader babel-loader babel-core babel-plugin-transform-runtime babel-preset-es2015 css-loader vue-style-loader vue-hot-reload-api vue-html-loader

3、在webpack.config文件夹中创建dev.js文件:

const path = require('path')
const webpack = require('webpack')
const merge = require('webpack-merge')
const baseConfig = require('./base')
const root = path.resolve(__dirname, '..')

module.exports = merge(baseConfig, {})

上面代码仅仅是导出了跟base.js一模一样的配置,下面我们添加更多用于dev(开发环境)的配置。

webpack-merge 用于合并两个配置文件,需要安装

npm install --save-dev webpack-merge

4、使用webpack-dev-server,开启一个小型服务器,不需要再手动打开index.html进行调试了,修改配置文件为:

module.exports = merge(baseConfig, {
  devServer: {
    historyApiFallback: true, // 404的页面会自动跳转到/页面
    inline: true, // 文件改变自动刷新页面
    port: 8080, // 服务器端口
  },
  devtool: 'source-map' // 用于标记编译后的文件与编译前的文件对应位置,便于调试
})

5、添加热替换配置,每次改动文件不会再整个页面都刷新,并使用HtmlWebpackPlugin,实现js入口文件自动注入

安装webpack-dev-server: npm install --save-dev webpack-dev-server

// ...同上
const HtmlWebpackPlugin = require('html-webpack-plugin')  // 实现js入口文件自动注入
module.exports = merge(baseConfig, {
  entry: [
    'webpack/hot/dev-server', // 热替换处理入口文件
    path.join(root, 'src/index.js')
  ],
  devServer: { /* 同上 */ },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),   // 添加热替换插件
    new HtmlWebpackPlugin({
      template: path.join(root, 'index.html'),  // 模板文件
      inject: 'body' // js的script注入到body底部
    })
  ]
})

这里的HotModuleReplacementPlugin是webpack内置的插件,不需要安装
但HtmlWebpackPlugin需要自行安装:

npm install --save-dev html-webpack-plugin

在文件头中引入

const HtmlWebpackPlugin = require('html-webpack-plugin')

修改index.html,去掉入口文件的javascript引入:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>myProject</title>
  </head>
  <body>
    <div id="app">{{ message }}</div>
  </body>
</html>

6、最后修改package.json中的webpack运行脚本为:

{
  "dev": "webpack-dev-server --config webpack.config/dev.js"
}

为了测试webpack配置是否都生效了,下面创建一个vue组件src/components/Hello.vue:

<template>
  <div>{{ message }}</div>
</template>

<script>
  export default {
    data: () => ({ message: 'Hello Vue.js!' })
  }
</script>

既然用到了vue组件,那自然需要安装vue-template-compiler: npm install --save-dev vue-template-compiler

修改index.js

import Vue  from 'vue'
import Hello from './components/Hello.vue'

new Vue({
  el: '#app',
  template: '<div><hello></hello></div>',
  components: { Hello }
})

运行npm run dev,打开浏览器localhost:8080,查看结果:

Hello Vue.js!

如果这里出现了报错,而且长这个样子的:

Error: Cannot find module 'webpack/bin/config-yargs'
    at Function.Module._resolveFilename (module.js:485:15)
    at Function.Module._load (module.js:437:25)
    at Module.require (module.js:513:17)
    ······balabala

那是因为webpackwebpack-dev-server的版本不兼容导致的,查看下版本重新下载就好了。

配置路由

1、安装vue-router: npm install --save vue-router

2、在src目录下创建views(用于存放页面组件)和router(用于存放所有路由相关的配置)文件夹。

3、添加页面组件src/views/Home.vue

<template>
  <div><hello></hello></div>
</template>

<script>
  import Hello from 'components/Hello'
  export default {
    components: { Hello }
  }
</script>

下面的几个文件因为是使用的新版环境,所以和原文的会有很大出入。

添加项目路由配置文件src/router/routes.js

import Home from 'views/Home'

export default [{
  path: '/',        // 代表访问 '/' 的时候其实是访问Home.vue页面
  name: 'home',
  component: Home
}]

添加路由入口文件src/router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import routes from './routes'

Vue.use(Router)

const router = new Router({
  hashbang: false, // 关闭hash模式
  history: true, // 开启html5 history模式
  linkActiveClass: 'active', // v-link激活时添加的class,默认是`v-link-active`
  routes
})

// 全局导航钩子
router.beforeEach((to, from, next) => {
  console.log('---------> ' + to.path)  // 每次调整路由时打印,便于调试
  next()
})

export default router

修改src/index.js

import Vue  from 'vue'
import router from './router'

const App = new Vue({
  router
}).$mount('#app')

最后别忘了index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>myProject</title>
</head>
<body>
  <div id="app">
    <router-view></router-view>    <!--路由替换位置-->
  </div>
</body>
</html>

重新执行npm run dev,打开浏览器localhost:8080(访问Home.vue页面)查看效果

Hello Vue.js

配置Vuex

vuex通常用于存放和管理不同组件中的共用状态,例如不同路由页面之间的公共数据
vuex中的几个概念:

  • state: 状态,即数据
  • store: 数据的集合,一个vuex引用,仅有一个store,包含n个state
  • getters:state不能直接取值,使用getters返回需要的state
  • mutations: state不能直接赋值,通过mutation定义最基本的操作
  • actions: 在action中调用一个或多个mutation
  • modules: store和state之间的一层,便于大型项目管理,store包含多个module,module包含state、mutation、action

本教程将以一个全局计数器作为例子

1、安装vuex: npm install --save vuex

2、添加src/store文件夹,存放vuex相关文件,添加src/store/modules用于vuex分模块管理。

添加src/store/types.js,vuex的所有mutation type都放在一起,不建议分开多个文件,有效避免重名情况:

export const INCREASE = 'INCREASE' // 累加
export const RESET = 'RESET' // 清零

3、编写vuex模块,添加counter.js模块目录store/modules/counter.js

import * as types from 'store/types'
// state
const state = {
  count: 0
}
// getters
const getters = {
  getCount: state => state.count
}
// actions
const actions = {
  increase({ commit }) {
    commit(types.INCREASE)  // 调用type为INCREASE的mutation
  },
  reset({ commit }) {
    commit(types.RESET)     // 调用type为RESET的mutation
  }
}
// mutations
const mutations = {
  [types.INCREASE](state) {
    state.count++
  },
  [types.RESET](state) {
    state.count = 0
  }
}

export default {
  state,
  getters,
  actions,
  mutations
}

4、添加store/index.js,作为vuex入口文件(共用的getters和actions可拆分到store/getters.jsstore/actions.js,然后在store/index.js进行导入,这里因为没有,所以就直接写了):

import Vue from 'vue'
import Vuex from 'vuex'
import counter from './modules/counter'

Vue.use(Vuex) // 确保在new Vuex.Store()之前

export default new Vuex.Store({
  getters: {},
  actions: {},
  modules: {
    counter
  }
})

5、修改src/index.js,将store引入并添加到App中:

import Vue from 'vue'
import router from './router'
import store from 'store'

const app = new Vue({
  router,
  store
}).$mount('#app')

6、最后改造一下components/Hello.vue,可以看到vuex v2变化了蛮多:

<template>
<div>
  <p>{{ message }}</p>
  <p>click count: {{ getCount }}</p>
  <button @click="increase">increase</button>
  <button @click="reset">reset</button>
</div>
</template>

<script>
// vuex 提供了独立的构建工具函数 Vuex.mapGetters, Vuex.mapActions
import { mapGetters, mapActions } from 'vuex'

export default {
  data: () => {
    return {
      message: 'Hello Vue.js!'
    }
  },
  computed: mapGetters({
    getCount: 'getCount'
  }),
  methods: mapActions([
    'increase',
    'reset'
  ])
}
</script>

重新执行npm run dev,打开浏览器localhost:8080,查看效果。

配置eslint

这不是必须的,不想要可以跳过。

虽然eslint不是必须的,但是强烈建议用在所有的JavaScript项目中。
对于个人开发,可以在编程过程中发现并提示语法错误,有效过滤各种低级错误
对于团队开发,强制采用一致的编码风格,保证项目的一致性,有效避免各种任性行为
但是一定要注意,eslint定义的只是编码风格,规则是死的,人是活的,学会利用自定义规则的功能,增减规则
同时要知道,eslint检测不通过不一定就是不能运行的,可能只是这种写法违背了编码风格,学会查看控制的查找具体错误原因
想要更好的eslint体验,请根据不同编辑器安装对应的eslint插件,主流的编辑器已有相应的插件

1、选择合适的编码风格

eslint提供了很多rules,可以直接在.eslintrc文件的rules中一个一个地配置
显然我们大多数情况下不需要这么做,晚上已经有一些比较多人使用的风格了,本文推荐使用standard,点开这个链接,可以看到支持的编辑器和对应的插件名,自行去安装

2、配置.eslintrc文件,在根目录下创建.eslintrc文件:

{
  "parser": "babel-eslint", // 支持babel
  "extends": "standard", // 使用eslint-config-standard的配置
  "plugins": [
    "html" // 支持.vue文件的检测
  ],
  "env": {
    "browser": true, // 不会将window上的全局变量判断为未定义的变量
    "es6": true // 支持es6的语法
  },
  "rules": { // 自定义个别规则写在这,0忽略,1警告,2报错
    "no-unused-vars": 1 // 将”未使用的变量“调整为警告级别,原为错误级别,更多规则请看官网
  }
}

结合不同编辑器的插件,打开js和vue文件中,就可以看到提示了

根据使用的不同风格,安装所需的包,本文安装:

npm install --save-dev eslint babel-eslint eslint-config-standard eslint-plugin-standard eslint-plugin-html eslint-plugin-promise

webpack生产环境配置

前面已经配置过了开发环境下使用的配置文件dev.js,对于生产环境,通常需要对编译出来的文件户必须不过压缩处理,提取公共模块等,就需要专门提供一个配置文件。

1、添加webpack.config/pro.js文件,把生产环境用不到的删除,比如webpack-dev-server, webpack-hot-replacement

const path = require('path')
const webpack = require('webpack')
const merge = require('webpack-merge')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const baseConfig = require('./base')
const root = path.resolve(__dirname, '..')

module.exports = merge(baseConfig, {
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(root, 'index.html'), // 模板文件
      inject: 'body' // js的script注入到body底部
    })
  ]
})

webpack常用插件

  • extract-text-webpack-plugin 提取css到单独的文件
  • compression-webpack-plugin 压缩gzip
  • webpack.optimize.UglifyJsPlugin 压缩js文件,内置插件
  • webpack.DefinePlugin 定义全局变量,内置插件
  • webpack.optimize.CommonsChunkPlugin 提取公共依赖,内置插件

根据项目需求添加相应的插件,插件配置参数请查看官方文档,这里不进行罗列

2、在package.json中添加脚本:"build": "webpack --config webpack.config/pro.js"

3、运行npm run build,可以在dist文件夹中看到打包好的文件。

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published