vuex2.0-例子學習-counter_byKL

vuex2.0 例子學習-counter

由於下載的測試demo是https://github.com/vuejs/vuex/tree/dev/examples,因此裏面文件比較多,這裏截取一部分跟counter的demo部分相關的內容作學習.css

  • counter的demo的主要文件是如下這些html

├── ./counter
│   ├── ./counter/Counter.vue
│   ├── ./counter/app.js
│   ├── ./counter/index.html
│   └── ./counter/store.js
  • 要使用webpack和babel和npm,並且他們分別在外層的項目的根目錄中,可是考慮本次demo他們不是主角,因此沒有太過詳細描述,不過須要知道的是,要理解vuex,就必需要掌握一部分的es6語法和webpack打包和babel轉譯es5的知識,否則看起來會一頭霧水vue

  • 另外就是MVVM的設計,對於vuex來講,在學習demo的時候要知道哪裏的代碼放在哪裏,爲何放在這裏,其實就是跟MVVM有關node

npm

稍稍介紹一下npm,在本次demo學習中,是使用npm對一塊兒來打包和轉譯等操做進行腳本化處理的webpack

package.json 在項目的跟位置,可是counter的demo是項目的一個example子目錄git

//其餘目錄能夠暫時不關注,不影響學習
.babelrc  //這裏是babel的配置文件,也是在項目根目錄
├── LICENSE
├── README.md
├── bower.json
├── build
├── circle.yml
├── dist
├── docs
├── examples  //裏面其中一個就是counter
    ├── chat
    ├── counter
    ├── counter-hot
    ├── global.css
    ├── index.html
    ├── server.js
    ├── shopping-cart
    ├── todomvc
    └── webpack.config.js
├── node_modules
├── package.json // 在這裏
├── src
├── test
├── types
└── yarn.lock

查看package.json:es6

{
  "name": "vuex",
  "version": "2.1.3",
  "description": "state management for Vue.js",
  "main": "dist/vuex.js",
  "typings": "types/index.d.ts",
  "files": [
    "dist",
    "src",
    "types/index.d.ts",
    "types/helpers.d.ts",
    "types/vue.d.ts"
  ],
  "scripts": {
    "dev": "node examples/server.js", // 主要關注這裏,咱們要作開發須要測試就是用這個命令
    "dev:dist": "rollup -wm -c build/rollup.config.js",
    "build": "npm run build:main && npm run build:logger", //這是發版本build的
    "build:main": "rollup -c build/rollup.config.js && uglifyjs dist/vuex.js -cm --comments -o dist/vuex.min.js",
    "build:logger": "rollup -c build/rollup.logger.config.js",
    "lint": "eslint src test",
    "test": "npm run lint && npm run test:types && npm run test:unit && npm run test:e2e",
    "test:unit": "rollup -c build/rollup.config.js && jasmine JASMINE_CONFIG_PATH=test/unit/jasmine.json",
    "test:e2e": "node test/e2e/runner.js",
    "test:types": "tsc -p types/test",
    "release": "bash build/release.sh",
    "docs": "cd docs && gitbook serve",
    "docs:deploy": "cd docs && ./deploy.sh"
  },
...................
  "homepage": "https://github.com/vuejs/vuex#readme",
  "devDependencies": { //瞭解一下這個demo須要的那些依賴模塊
    "babel-core": "^6.22.1", 
    "babel-eslint": "^7.1.1",
    "babel-loader": "^6.2.10",
    "babel-plugin-transform-runtime": "^6.22.0",
    "babel-polyfill": "^6.22.0", //將es6轉譯es5的api的babel
    "babel-preset-es2015": "^6.22.0", //將es6轉譯es5的babel
    "babel-preset-es2015-rollup": "^3.0.0",
    "babel-preset-stage-2": "^6.22.0", 
    "babel-runtime": "^6.22.0",
    "chromedriver": "^2.27.2",
    "cross-spawn": "^5.0.1",
    "css-loader": "^0.26.1", //webpack處理css的工具
    "eslint": "^3.15.0",
    "eslint-config-vue": "^2.0.2",
    "eslint-plugin-vue": "^2.0.1",
    "express": "^4.14.1",
    "jasmine": "2.5.3",
    "jasmine-core": "2.5.2",
    "nightwatch": "^0.9.12",
    "nightwatch-helpers": "^1.2.0",
    "phantomjs-prebuilt": "^2.1.14",
    "rollup": "^0.41.4",
    "rollup-plugin-buble": "^0.15.0",
    "rollup-plugin-replace": "^1.1.1",
    "rollup-watch": "^3.2.2",
    "selenium-server": "^2.53.1",
    "todomvc-app-css": "^2.0.6",
    "typescript": "^2.1.5",
    "uglify-js": "^2.7.5",
    "vue": "^2.1.10", //vue庫
    "vue-loader": "^11.0.0", //*.vue文件的處理的
    "vue-template-compiler": "^2.1.10",
    "webpack": "^2.2.1",
    "webpack-dev-middleware": "^1.10.0",
    "webpack-hot-middleware": "^2.16.1" //webpack的熱加載,就是每次修改都能自動加載到瀏覽器
  }
}
  1. 除了主要關注部分,其餘稍稍瞭解一下就行了,通常都是webpack打包的一些基本插件,多出來的都是有其餘特殊用途的,但跟vuex自己關係不大github

  2. 這裏並無vuex的包,主要是由於這個github demo裏有vuex的源代碼,而後在編譯的時候直接使用vuex的源代碼來運行了web

webpack

這裏有2個文件,server.js和webpack.config.jsvuex

server.js

  • 由於在package.json裏面指定了開發的時候npm run dev會執行這個文件

  • 這個執行這個文件會啓動一個本地web server,監聽8080端口

const express = require('express')
const webpack = require('webpack')
const webpackDevMiddleware = require('webpack-dev-middleware') //webpack經過這個模塊去捕獲到內存中,方便開發使用
const webpackHotMiddleware = require('webpack-hot-middleware') //會使用熱加載模塊
const WebpackConfig = require('./webpack.config') //加載webpack配置文件

const app = express()
const compiler = webpack(WebpackConfig)

app.use(webpackDevMiddleware(compiler, {
  publicPath: '/__build__/', //webpackDevMiddleware的公共目錄,在dev模式下,瀏覽器可使用這個位置引用文件,例如引用js
  stats: {
    colors: true,
    chunks: false
  }
}))

app.use(webpackHotMiddleware(compiler))

app.use(express.static(__dirname))

const port = process.env.PORT || 8080 //本地webserver服務器的8080端口
module.exports = app.listen(port, () => {
  console.log(`Server listening on http://localhost:${port}, Ctrl+C to stop`)
})
  1. 關於webpack dev server 這裏有一個回答蠻好的:從頭說起的話就是 webpack 自己只負責打包編譯的功能 bundle, webpack-dev-server 當然就是協助我們開發的伺服器,這個伺服器底層是靠 express 來實做的,接著思考一下我們要如何更新(live reload)呢? 當然是須要取得 webpack 編好的資料啊,於是就須要在從 request 到 response 的過程中透過 express 的 middleware 取得資料,而方法就是透過 webpack-dev-middleware 。

  2. 熱加載就是那種相似live reload的東西,自動刷新.

webpack.config.js

webpack的配置文件

const fs = require('fs')
const path = require('path')
const webpack = require('webpack') //引用了webpack模塊

module.exports = {

  devtool: 'inline-source-map',

  entry: fs.readdirSync(__dirname).reduce((entries, dir) => {
    const fullDir = path.join(__dirname, dir)
    const entry = path.join(fullDir, 'app.js') //使用app.js做爲入口,後面能夠看到app.js的內容,這裏只須要知道這一個總入口
    if (fs.statSync(fullDir).isDirectory() && fs.existsSync(entry)) {
      entries[dir] = ['webpack-hot-middleware/client', entry]
    }

    return entries
  }, {}),

  output: {
    path: path.join(__dirname, '__build__'), 
    filename: '[name].js', //通常的js就按照名字命名
    chunkFilename: '[id].chunk.js', //這個是暫時用不到
    publicPath: '/__build__/' //publicPath指定了你在瀏覽器中用什麼地址來引用你的靜態文件,它會包括你的圖片、腳本以及樣式加載的地址,通常用於線上發佈以及CDN部署的時候使用。
  module: {
    rules: [
      { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }, //js會被babel-loader捕獲,而後解析翻譯
      { test: /\.vue$/, loader: 'vue-loader' } //.vue文件會被vue-loader解析翻譯
    ]
  },

  resolve: {
    alias: { //這裏創建了一個別名vuex,而後指向源代碼目錄來調用vuex,因此在package.json裏面沒看到vuex,由於他在這裏調用了源代碼的vuex
      vuex: path.resolve(__dirname, '../build/dev-entry')
    }
  },

  plugins: [
    new webpack.optimize.CommonsChunkPlugin({ //將公共部分輸出到指定的js裏面,給公共使用
      name: 'shared', //給這個包含公共代碼的chunk命個名(惟一標識)。
      filename: 'shared.js' //命名打包後生產的js文件
    }),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin()
  ]

}
  1. CommonsChunkPlugin的效果是:在你的多個頁面(入口)所引用的代碼中,找出其中知足條件(被多少個頁面引用過)的代碼段,斷定爲公共代碼並打包成一個獨立的js文件。至此,你只須要在每一個頁面都加載這個公共代碼的js文件,就能夠既保持代碼的完整性,又不會重複下載公共代碼了(多個頁面間會共享此文件的緩存)。引用參考:webpack.optimize.CommonsChunkPlugin怎麼打包公共代碼才能避免重複?

  2. chunkFilename用來打包require.ensure方法中引入的模塊,本次demo並無這種方法引入的模塊,因此不會打包出來,引用參考

babel

稍稍提一下.babelrc,babel的配置文件,由於webpack+babel通常都是連着使用了,因此也須要大概瞭解一下

{
  "presets": [
    ["es2015", { "modules": false }], //使用將es6轉譯爲es5的插件
    "stage-2" //使用將es6的stage-2的語法轉譯爲es5的插件
  ],
  "plugins": ["transform-runtime"], //這個相對有點複雜,大概知道意思便可
  "comments": false,
  "env": {
    "test": {
      "plugins": [ "istanbul" ]
    }
  }
}

大概瞭解一下下......
transform-runtime通常跟babel-polyfill連用,目的是模擬出原生瀏覽器的全部功能的環境

  1. babel-runtime 的做用是模擬 ES2015 環境,包含各類分散的 polyfill 模塊

  2. babel-polyfill 是針對全局環境的,引入它瀏覽器就好像具有了規範裏定義的完整的特性,一旦引入,就會跑一個 babel-polyfill 實例。

  3. 這兩個模塊功能幾乎相同,就是轉碼新增 api,模擬 es6 環境,但實現方法徹底不一樣。babel-polyfill 的作法是將全局對象統統污染一遍,好比想在 node 0.10 上用 Promise,調用 babel-polyfill 就會往 global 對象掛上 Promise 對象。對於普通的業務代碼沒有關係,但若是用在模塊上就有問題了,會把模塊使用者的環境污染掉。

引用:https://zhuanlan.zhihu.com/p/20904140?refer=mirreal
引用:https://github.com/brunoyang/blog/issues/20

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>vuex counter example</title>
    <link rel="stylesheet" href="/global.css">
  </head>
  <body>
  <!--vue實例的綁定位置-->
    <div id="app"></div> 
    <!--兩個編譯出來的js文件,名字和目錄是webpack裏面指定的-->
    <script src="/__build__/shared.js"></script>
    <script src="/__build__/counter.js"></script>
  </body>
</html>

app.js

import 'babel-polyfill' //引入babel-polyfill,以前已經引入了transform-runtime,可是由於他要調用babel-polyfill,因此要另外import
import Vue from 'vue' //引入vue.js
import Counter from './Counter.vue' //引入Counter.vue
import store from './store' //引入store.js

new Vue({ //初始化vue實例
  el: '#app',
  store, //這個store是上面的引入的store.js
  render: h => h(Counter) //用render函數來渲染,而且渲染的是Counter函數
})

這裏有個地方須要注意,在es6裏,import的時候會自動提高執行優先級,也就是會提高到當前模塊的頭部,那麼這裏即便先import Counter再import store,在Counter裏面依然能夠調用store裏面的屬性或者方法

Counter.vue

這是vue的文件寫法,template,script,css的結構,這裏忽略了css

<template>
  <div id="app">
    //而且這裏使用了$store.state.count的值,直接訪問store裏面的值
    Clicked: {{ $store.state.count }} times, count is {{ evenOrOdd }}. //能夠evenOrOdd計算屬性
    <button @click="increment">+</button> //能夠直接使用increment方法
    <button @click="decrement">-</button>
    <button @click="incrementIfOdd">Increment if odd</button>
    <button @click="incrementAsync">Increment async</button>
  </div>
</template>

<script>
import { mapGetters, mapActions } from 'vuex' //從vuex導入 mapGetters, mapActions

export default { //導出默認的對外接口
  computed: mapGetters([ //mapGetters輔助函數僅僅是將 store 中的 getters 映射到局部計算屬性:
    'evenOrOdd'
  ]),
  methods: mapActions([ //相似,而且映射以後能夠在當前vue組件使用
    'increment',
    'decrement',
    'incrementIfOdd',
    'incrementAsync'
  ])
}
</script>

store.js

反而這個store.js沒什麼好看的,對比vuex的官網文檔幾乎都能找到解釋

import Vue from 'vue' //引入vue
import Vuex from 'vuex' //引入vuex

Vue.use(Vuex) //vue使用vuex

// root state object.
// each Vuex instance is just a single state tree.
const state = { 
  count: 0
}

// mutations are operations that actually mutates the state.
// each mutation handler gets the entire state tree as the
// first argument, followed by additional payload arguments.
// mutations must be synchronous and can be recorded by plugins
// for debugging purposes.
const mutations = { 
  increment (state) {
    state.count++
  },
  decrement (state) {
    state.count--
  }
}

// actions are functions that causes side effects and can involve
// asynchronous operations.
const actions = {
  increment: ({ commit }) => commit('increment'),
  decrement: ({ commit }) => commit('decrement'),
  incrementIfOdd ({ commit, state }) {
    if ((state.count + 1) % 2 === 0) {
      commit('increment')
    }
  },
  incrementAsync ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('increment')
        resolve()
      }, 1000)
    })
  }
}

// getters are functions
const getters = { 
  evenOrOdd: state => state.count % 2 === 0 ? 'even' : 'odd'
}

// A Vuex instance is created by combining the state, mutations, actions,
// and getters.
export default new Vuex.Store({
  state,
  getters,
  actions,
  mutations
})

參考引用:

  1. https://github.com/vuejs/vuex/tree/dev/examples

  2. http://www.imooc.com/article/10969

  3. https://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin

  4. http://vuex.vuejs.org/zh-cn/getters.html

相關文章
相關標籤/搜索