2019年10月21日 星期一

[Vue] 路由

簡單路由

  • Step1: 定義路由
const routes = {
  '/': Home,
  '/about': About
}
  • Step2: 在root設定
new Vue({
  el: '#app',
  data: {
    currentRoute: window.location.pathname
  },
  computed: {
    ViewComponent () {
      return routes[this.currentRoute] || NotFound
    }
  },
    render (h) { return h(this.ViewComponent) }
})

完整的官方Vue Router

https://router.vuejs.org/zh/

[Vue Loader] 簡介

Vue Loader

Vue Loader 是 webpack 的 loaderoader,可以幫助開發者用組件的概念開發 vue 專案。

若不想使用,想要達到同樣效果,可直接使用 vue cli 建立專案。

使用步驟

  • 建立npm套件記錄檔
npm init y
  • 安裝webpack
npm install webpack webpack-cli --save-dev
  • 建立預設資料夾
mkdir src
mkdir dist
  • 加入指令至package.json
"build": "webpack"
  • 加入 dist 中的 index.html
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="app"></div>
    <script src="main.js"></script>
</body>

</html>
  • 加入 index.js 至 src 中
  • webpack基礎架構完成,後續進入vue環境設定
  • 安裝vue loader
npm install -D vue-loader vue-template-compiler
  • 加入web.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
  mode: 'development',
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      // 它会应用到普通的 `.js` 文件
      // 以及 `.vue` 文件中的 `<script>` 块
      {
        test: /\.js$/,
        loader: 'babel-loader'
      },
      // 它会应用到普通的 `.css` 文件
      // 以及 `.vue` 文件中的 `<style>` 块
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    // 请确保引入这个插件来施展魔法
    new VueLoaderPlugin()
  ]
}
  • 加入 Vue root 至  index.js 中(記得安裝vue, npm install vue --D)
import Vue from 'vue'

new Vue({
    el: '#app',
    mounted: function () {
        console.log('Hello World');

    }
})
  • 加入其他轉譯相關套件
npm install babel-core babel-loader --save-dev
npm install  vue-style-loader css-loader --save-dev

*. babel-loader改成^7.1.5
  • 加入 resolve 至 webpack config 中

resolve: { 
            alias: { 
                'vue': 'vue/dist/vue.js' 
            } 
        }
  • 使用babel預設參數
npm install babel-preset-env babel-preset-vue --save-dev

加入以下設定至 package.json 中

"babel": {
    "presets": [
      "env",
      "vue"
    ]
  }

2019年10月17日 星期四

[Vue CLI] 單文件組件


  • Vue.component是global組件
  • new Vue({    el: '#app' ... }) 可定義容器
通常透過以上2點就可構成 vue 的前台站台,為了方便管理,可用單文件組件的概念來開發。

單文件組件

  • 副檔名: *.vue
  • <template></template>中放html
  • <script></script>中放js
  • <style></style>中放css
  • 同一份檔案包含三個區塊

關注點分離

關注點分離不代表文件類型分離,和其他的框架認知上有根本上的差異

Vue Loader

.vue文件需要透過 vue loader 編譯成最後結果

2019年10月16日 星期三

[Vue CLI] CLI服務

@vue/cli-service

這包程式碼提供了開發、打包的指令。

使用vue cli建立的專案內建二了指令

{
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build"
  }
}

vue-cli-service serve [options] [entry]

啟動開發站台

options
  •   --open    在服務器啟動時打開瀏覽器
  •   --copy    在服務器啟動時將 URL 複製到剪切版
  •   --mode    指定環境模式 (默認值:development)
  •   --host    指定 host (默認值:0.0.0.0)
  •   --port    指定 port (默認值:8080)
  •   --https   使用 https (默認值:false)

vue-cli-service build [options] [entry|pattern]

打包程式碼

options
  •   --mode        指定環境模式 (默認值:production)
  •   --dest        指定輸出目錄 (默認值:dist)
  •   --modern      面向現代瀏覽器帶自動回退地構建應用
  •   --target      app | lib | wc | wc-async (默認值:app)
  •   --name        庫或 Web Components 模式下的名字 (默認值:package.json 中的 "name" 字段或入口文件名)
  •   --no-clean    在構建項目之前不清除目標目錄
  •   --report      生成 report.html 以幫助分析包內容
  •   --report-json 生成 report.json 以幫助分析包內容
  •   --watch       監聽文件變化

vue-cli-service inspect [options] [...paths]

檢查 vue cli 的 web config

[Vue CLI] 預設配置

.vuerc

在 windows 作業系統中,User/安裝過vue的登入者底下,會有一個.vuerc的檔案,裡面紀錄用vue create建立專案時,的預設配置。

2019年10月14日 星期一

[Vue CLI] 插件

vue ui

可用圖形化介面了解整個框架的全貌

vue add

  • 可在現存的vue專案中,加入第三方套件
  • 主要目的為控管vue專案的第三方套件,跟vue無關的套件不要用vue add安裝
e.g. vue add eslint

router和vuex

  • 加入router
vue add router

*. 內件基本架構,使用非常方便
  • 加入vuex
vue add vuex

vue invoke

套件已安裝,但是想改參數的時候,可以使用這個命令

e.g. 

之前已經做過這個指令 vue add eslint,但是想改eslint的參數

可以下這個指令 vue invoke eslint --config airbnb --lintOn save

2019年10月13日 星期日

[Vue CLI] 安裝

安裝Vue CLI

npm install -g @vue/cli

安裝完成後檢查版本

vue --version

建立專案

  • 命令安裝
vue create project-name
  • 介面安裝(易上手)
vue ui

快速原型開發

  • 需額外安裝service
npm install -g @vue/cli-service-global

  • 安裝完可使用以下指令
  1. vue serve
  2. vue build

2019年10月6日 星期日

[Vue] 動態組件&異步組件

動態組件

<component v-bind:is="currentTabComponent"></component>
  • 在切換component時, 預設會重新渲染DOM
  • <keep-alive></keep-alive> 可以不用重新渲染DOM
<keep-alive>
    <component v-bind:is="currentTabComponent"></component>
</keep-alive> 

異步組件

  • 在非同步, 有條件的情境下, 載入組件
Vue.component('function1', function (resolve, reject) {
    setTimeout(() => {
        resolve({
            template: `
            <div>        
                <counter></counter>
                <counter></counter>
            </div>`
        })
    }, 3000);
})

延伸關鍵字 webpack + async vue component

2019年10月3日 星期四

[Vue] 插槽 2/2

作用域插槽

  1. 父組件可以使用子組件的data
  2. 被綁定在slot上的prop稱為: 插槽 prop
  • 父組件
<my-tab>
</my-tab>
  • 子組件
Vue.component('my-tab', {
    data: function () {
        return {
            tabName: 'Default Tab Name'
        }
    },
    template: `
    <div>
    <br>
    <a style="border: 1px solid black;">
        <slot v-bind:tabName="tabName">預設內容</slot>
    </a>
    <br>
    <slot name="content"></slot>
    </div>
    `
})

自定義插槽prop名稱

  1. 在子組件上, 用v-slot可自定義插槽prop的名稱
  2. 簡寫: <my-tab v-slot="myChildVariable">...</my-tab>
  3. 簡寫和作用愈插槽寫法不可混用
  • 父組件
        <my-tab>
            <template v-slot:default="childProp">
                {{childProp.tabName}}
            </template>
        </my-tab>
  • 子組件
Vue.component('my-tab', {
    data: function () {
        return {
            tabName: 'Default Tab Name'
        }
    },
    template: `
    <div>
    <br>
    <a style="border: 1px solid black;">
        <slot v-bind:tabName="tabName">預設內容</slot>
    </a>
    <br>
    <slot name="content"></slot>
    </div>
    `
})

解構插槽prop

1. 插槽原理: 將插槽內容包括在一個傳入單個參數的函數裡,如下

function (props) {
  // 插槽內容
}

所以可以套入"解構"的概念,控制子組件的屬性
<my-tab>
    <template v-slot="{tab}">
        {{tab.tabName2}}
    </template>
</my-tab>

2. 解構之餘,同時也可重新命名屬性
<my-tab>
    <template v-slot="{tab: myTabCollection}">
        {{myTabCollection.tabName2}}
    </template>
</my-tab>

*.v-slot的縮寫符號: #


2019年10月2日 星期三

[Vue] 插槽 1/2

<slot></slot>

  1. 可以在自定義的component中,預留一個空間,給父組件插入內容。
  2. 子組件和父組件的作用域各自獨立,不可交互使用。
  • 子組件
Vue.component('my-tab', {
    template:`
    <a style="border: 1px solid black;">
        <slot></slot>
    </a>
    `
})
  • 父組件
<my-tab>
     slot content
     <br>
    {{currentTab}}
</my-tab>

後備內容

<slot></slot>中可以設定預設內容,在無任何資料時,自動顯示
  • 子組件
Vue.component('my-tab', {
    template:`
    <a style="border: 1px solid black;">
        <slot>預設內容</slot>
    </a>
    `
})
  • 父組件
<my-tab>
     slot content
     <br>
    {{currentTab}}
</my-tab>

具名插槽

  1. slot可以給定名稱(name),就可以預留多個空間給父組件。
  2. 沒有名稱(name)的slot,預設名稱: default。
  • 子組件
Vue.component('my-tab', {
    template:`
    <div>
    <a style="border: 1px solid black;">
        <slot>預設內容</slot>
    </a>
    <br>
    <slot name="content"></slot>
    </div>
    `
})
  • 父組件
<my-tab>
    slot content
    <br>
    {{currentTab}}
    <template v-slot:content>
        具名slot,給定名稱content,插入指定位置
    </template>
</my-tab>

2019年10月1日 星期二

[Vue] 自定義事件

自定義事件命名

  • 駝峰命名不會自動轉換成kebab-case,盡量使用kebab-case
e.g.

this.$emit('myCustomEvent');

// 這行不會有效果
<component v-on:my-custom-event="onEvent"></component>
  • 但是會自動轉成小寫
上面那個宣告需要使用以下寫法才監聽的到
<component v-on:myCustomEvent="onEvent"></component>

原生事件綁定至自製組件(進階)

1. 使用自製組件,綁定原生事件的作法
  • 父組件
<my-checkbox v-model="YesOrNo" v-on:focus.native="onFocus"></my-checkbox>
  • 子組件
<input
        type="checkbox"
        v-bind:checked="checked"
        v-on:change="$emit('change',$event.target.checked)"
    >
以上可以監聽子組件根元素(input)的focus事件,並觸發父組件的onFocus

2. 當根元素不是input,卻又想要做到上面這件事情時

$listeners(進階)

  • 父組件
<my-checkbox v-model="YesOrNo" v-on:click.native="onClick"></my-checkbox>
  • 子組件
computed: {
        checkboxListener: function () {
            var vm = this;
            return Object.assign({},
                this.$listeners,
                {
                    click: function (event) {
                        vm.$emit('click', event.target.checked)
                    },
                    change: function (event) {
                        vm.$emit('change', event.target.checked)
                    },
                })
        }
    },
    template: `
    <label>
    {{label}}
        <input
            type="checkbox"
            v-bind="$attrs"
            v-bind:checked="checked"         
            v-on="checkboxListener"
        >
    </label>
    `
關鍵字預留 : .sync