vue3.0使用流程和變化

安裝流程

1.安裝vite腳手架

npm install -g create-vite-app
# or
yarn add -g create-vite-app

2.項目初始化

yarn create vite-app <project-name>

3.集成TS

yarn add --dev typescript

4.項目根目錄創建配置文件:tsconfig.json:

{
  "include": ["./**/*.ts"],
  "compilerOptions": {
    "jsx": "react",
    "target": "es2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */,
    "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
    // "lib": ["es2017.object"] /* Specify library files to be included in the compilation. */,
    // "declaration": true /* Generates corresponding '.d.ts' file. */,
    // "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */
    "sourceMap": true /* Generates corresponding '.map' file. */,
    // "outFile": "./",                       /* Concatenate and emit output to single file. */
    "outDir": "./dist" /* Redirect output structure to the directory. */,

    "strict": true /* Enable all strict type-checking options. */,
    "noUnusedLocals": true /* Report errors on unused locals. */,
    "noImplicitReturns": true /* Report error when not all code paths in function return a value. */,

    "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
    "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
  }
}

5.集成eslint(如果不想安裝,可以忽略5,6)

yarn add --dev eslint eslint-plugin-vue

6.項目根目錄創建配置文件.eslintrc.js:

module.exports = {
  parser: 'vue-eslint-parser',
  parserOptions: {
    parser: '@typescript-eslint/parser', // Specifies the ESLint parser
    ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
    sourceType: 'module', // Allows for the use of imports
    ecmaFeatures: {
      // tsx: true, // Allows for the parsing of JSX
      jsx: true,
    },
  },
  // settings: {
  //   tsx: {
  //     version: "detect" // Tells eslint-plugin-react to automatically detect the version of React to use
  //   }
  // },
  extends: [
    'plugin:vue/vue3-recommended',
    'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin
    'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
    'plugin:prettier/recommended', // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
  ],
  rules: {
    // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
    // e.g. "@typescript-eslint/explicit-function-return-type": "off",
  },
};

7.集成pritter

yarn add --dev prettier eslint-config-prettier eslint-plugin-prettier

8.項目根目錄創建配置文件:.prettierrc.js:

module.exports = {
  semi: true,
  trailingComma: "all",
  singleQuote: true,
  printWidth: 100,
  tabWidth: 2,
  endOfLine:"auto"
};

到這一步,一個Vue3+TSX的項目就搭建起來了,以上配置文件的具體內容就不做解釋了。
9.修改入口文件

因為默認項目模板是以src/main.js為入口的,我們需要把它修改為src/main.ts。
在根目錄的index.html中修改入口文件的引用即可:

... ...
<body>
  ... ...
  <script type="module" src="/src/main.ts"></script>
</body>
</html>

10.優化TS類型推斷

在src目錄下,創建shim.d.ts、source.d.ts
shim.d.ts: (這個其實不太需要,因為項目中全是通過tsx開發的)

declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;
}

source.d.ts: (優化編譯器提示,聲明靜態資源文件)

declare const React: string;
declare module '*.json';
declare module '*.png';
declare module '*.jpg';

11.集成vue-router

yarn add --dev vue-router@4.0.0-beta.2

這里可以去npm官網查找最新版本
在src目錄下,新建router文件夾,并在文件夾內創建index.ts index.ts:

import { RouteRecordRaw, createRouter, createWebHistory } from 'vue-router';

const routes: RouteRecordRaw[] = [
  {
    path: '/',
    name: 'Home',
    component: import('../views/Home'),
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('../views/About'),
  },
];

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
});

export default router;

這里創建router的方式與之前不同,在vue3中,結合TS的類型推斷,開發效率會高很多。

12.集成vuex

yarn add --dev vuex@4.0.0-beta.4

在src目錄下,新建store文件夾,并在文件夾內創建index.ts
index.ts:

import { state } from './state';
import { createStore } from 'vuex';

export default createStore({
  state,
  mutations: {},
  actions: {},
  modules: {},
});

state.js:

export interface State {
  title: string;
}

export const state: State = {
  title: 'Vue(v3) 與 tsx 的結合~',
};

13.main.ts

最終main.ts中引入store、router:

import { createApp } from 'vue';
import Root from './App.vue';
import router from './router';
import store from './store';

const app = createApp(Root);
app.use(router);
app.use(store);
app.mount('#app');

export default app;

以上安裝流程來自Vue3全家桶 + Vite + TS + TSX嘗鮮,先人一步!

vue3.0發生的變化

vue3現在沒有this,官方的解釋:在 setup() 內部,this 不會是該活躍實例的引用,因為 setup() 是在解析其它組件選項之前被調用的,所以 setup() 內部的 this 的行為與其它選項中的 this 完全不同。這在和其它選項式 API 一起使用 setup() 時可能會導致混淆。那么問題來了沒有this我們如何使用router,store,emit, nextTick這些方法呢?下面會有案例來說明如何使用。

1.創建方式改變

vue創建方式

老版本

var app = new Vue({
  el: '#app',
  data: {}
})

新版本

import { createApp } from 'vue';
createApp(Root).mount('#app')

vuex創建方式

老版本

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

新版本

import { state } from './state.js';
import { createStore } from 'vuex';

export default createStore({
  state,
  mutations: {},
  actions: {},
  modules: {},
});

//使用方式
<script type="ts">
import {} from 'vue';
import { useStore } from 'vuex';
import axios from '../../../axios/index';
export default {
  name: 'NavMenu',
  props: {
  },
  setup(props, context) {
    const store = useStore();
    const getMenu = () => {
      const url = 'xxxx';
      axios('post', url).then((res) => {
        routerList.value = res;
        store.commit('setRouterList', res)
      });
    };
    return {
    };
  },
};
</script>

vue-router創建方式

老版本

import VueRouter from 'vue-router';
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User,
      children: [
        {
          path: 'profile',
          component: UserProfile
        },
        {
          path: 'posts',
          component: UserPosts
        }
      ]
    }
  ]
})

新版本

import { RouteRecordRaw, createRouter, createWebHashHistory } from 'vue-router';
const routes: RouteRecordRaw[] = [{
 path: '/',
    name: '首頁',
    component: getComponentsPath('Layout'),
    children: [{
      { path: '/', redirect: '/subassembly/base-subassembly' },
      {
        path: '/subassembly',
        name: '組件',
        component: view(),
        children: [
          {
            path: '/subassembly/base-subassembly',
            name: '基礎組件',
            component: getViewPath('subassembly/baseSubassembly'),
          },
          {
            path: '/subassembly/complexity-subassembly',
            name: '復雜組件',
            component: getViewPath('subassembly/complexitySubassembly'),
          },
        ],
      },
    }]
}]
const router = createRouter({
  history: createWebHashHistory(process.env.BASE_URL),//可以換成History 模式
  routes,
});

export default router;

2.寫法的改變
新版中需要注意的點都在注釋中

老版本data函數 => 新版本setup函數

老版本

<template>
  <div>{{num}}</div>
</template>

<script>
  export default {
  data() {
    return {
      num:0
    };
  },
}
</script>

<style lang='less' scoped>

</style>

新版本

<template>
  <div>{{num}}</div>
</template>

<script>
import { ref, reactive, toRef } from 'vue';
export default {
  setup() {
    //vue2.x中的data函數
    const num = 0; //不具備響應式特性
    const cunst = ref(0); //單個屬性具備響應式特性
  //如果想讓引用數據類型的值有響應式狀態就需要
  //const books = reactive([ref('Vue 3 Guide')]),下面的這種寫法
    const checkList = reactive([]);
    //多個屬性具備響應式特性
    const state = reactive({
      name: '張三',
      age: 18,
    });


    return {
      num,
      cunst,
      //因為setup本身是返回一個對象的如果有多個返回可以使用ES6的展開運算符,
      //但是reactive方法有bug對于直接把state放入return中會丟失響應式特效所以在返回之前用toRef方法包裹
      //toRef:簡單來講就是讓reactive中的值每個都具有響應式特性
      ...toRef(state),
    };
  },
};
</script>

<style lang='less' scoped>
</style>

生命周期
老版本

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

<script>
  export default {
  created() {

  },
  mounted() {

  },
  methods: {

  },
}
</script>

<style lang='less' scoped>

</style>

新版本

//生命周期函數,vue3.0刪除掉兩個生命周期
//beforeCreate -> use setup()
//created -> use setup()

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

<script>
import { ref, reactive, onMounted, onUpdated, onUnmounted, toRef } from 'vue';
export default {
  setup() {
    onMounted(() => {
      console.log('mounted!');
    });
    onUpdated(() => {
      console.log('updated!');
    });
    onUnmounted(() => {
      console.log('unmounted!');
    });

    return {
    };
  },
};
</script>

<style lang='less' scoped>
</style>

3.注冊全局組件
老版本

import Vue from 'vue';
import content from "./content.vue";
Vue.component("my-content ", content );

新版本

import myInput from './Input/index.vue';
import myRadio from './Radio/index.vue';
import myCheckbox from './Checkbox/index.vue';
export default {
  install: (Vue: any) => {
    Vue.component("MyComponentInput",myInput);
    Vue.component("MyComponentRadio",myRadio);
    Vue.component("MyComponentCheckbox",myCheckbox);
  },
};

main文件中引入

import components from './components/components';
const app = createApp(Root);
app.use(components);
app.mount('#app');

其實按照vue3的文檔上來看的話創建全局組件和vue2時沒有變化,但是我用的時候報錯,原因沒有找到,下面是vue3文檔的寫法

const app = Vue.createApp({...})

app.component('my-component-name', {
  /* ... */
})

4.Vue3.0配置全局屬性的方法
老版本
Vue 2.0之前我們封裝axios和message等通用屬性的時候是這么寫的:

Vue.prototype.$api = api
Vue.prototype.$http = http

新版本

const app=createApp(App);

app.use(store);
app.use(router);
app.use(Antd);
app.mount('#app');

// 配置全局屬性
app.config.globalProperties.$message = message;
app.config.globalProperties.$http=http;
app.config.globalProperties.$api=api;

//使用方式
import { getCurrentInstance } from "vue";
在setUp函數中通過 
const { ctx } = getCurrentInstance(); //獲取上下文實例,ctx=vue2的this   
ctx.$api ()
//或者
const { proxy } = getCurrentInstance(); 
proxy.http();  

vue3中使用Provide/Inject依賴注入,將替代vue2中在原型鏈上掛載一些屬性

 //APP.vue
   //在app.vue內注冊
    import { provide ,ref ,readonly} from "vue";
    setup() {
    //用ref包裹可使location變為響應式
        let location=ref('location')
        const updataLocation=()=>{
           location.value='變身后的大Location'
        }
        /*readonly包裹后可以在組件內引用時不被改變值。
        否則在組件內可以直接通過location.value=***將值改變,
        包裹后只能通過updataLocation()方法改變*/
        provide('location',readonly(location))
        provide('updataLocation',updataLocation)
       }

    //組件內使用
    import { inject} from "vue";
    setup(){
        const userLocation = inject('location')
        const updataLocation = inject('updataLocation')
        return{
        userLocation,updataLocation
        }
    }

5.vue3封裝input

//對于所有不帶參數的 v-model,請確保分別將 prop 和 event 命名更改為 modelValue 和 update:modelValue
//詳情查看https://vue-docs-next-zh-cn.netlify.app/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5

<template>
  <div>
    <input
      :class="dynamic"
      :type="type"
      :maxlength="maxlength"
      :minlength="minlength"
      :placeholder="placeholder"
      :disabled="disabled"
      @input="emitValue"
      :value="modelValue"
    />
  </div>
</template>

<script>
import { onMounted, reactive, computed, toRefs } from 'vue';
export default {
  name: 'MyComponentInput',
  props: {
    type: {
      tyep: String,
      default: () => 'text',
    },
    maxlength: {
      tyep: Number,
      default: 255,
    },
    minlength: {
      tyep: Number,
      default: 0,
    },
    placeholder: {
      tyep: String,
      default: '',
    },
    disabled: {
      tyep: Boolean,
      default: false,
    },
    size: {
      tyep: String,
      default: 'small',
    },
    modelValue: {
      tyep: String,
      default: '',
    },
  },
  setup(props, { emit }) {
    const size = props.size;
    const dynamic = computed(() => {
      let c = '';
      switch (size) {
        case 'large':
          c = 'large';
          break;
        case 'small':
          c = 'small';
          break;
        case 'mini':
          c = 'mini';
          break;
      }
      return c;
    });
    onMounted(() => {
    });
    const emitValue = (e) => {
      let value = e.target.value;
      emit('update:modelValue', value);
    };
    return {
      dynamic,
      emitValue,
    };
  },
};
</script>

<style lang='less' scoped>
input {
  background-color: #fff;
  background-image: none;
  border-radius: 4px;
  border: 1px solid #dcdfe6;
  box-sizing: border-box;
  color: #606266;
  display: inline-block;
  font-size: inherit;
  height: 40px;
  line-height: 40px;
  outline: none;
  padding: 0 15px;
  transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
  width: 100%;
}
.large {
  height: 36px;
  line-height: 36px;
}
.small {
  height: 32px;
  line-height: 32px;
}
.mini {
  height: 28px;
  line-height: 28px;
}
</style>

可以看出來現在不需要在用model來設置props和event,外面也不需要在用.snyc來接值

6.如何使用路由跳轉和vue內置方法

setup函數中有兩個參數,props,context
context中有attrs,slots,emit

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

<script>
import { } from 'vue';
export default {
  setup(props,context) {

    return {
    };
  },
};
</script>

<style lang='less' scoped>
</style>

使用emit方法

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

<script>
import {} from 'vue';
export default {
  setup(props, { emit }) {
    emit('add', 123);
    return {};
  },
};
</script>

<style lang='less' scoped>
</style>

使用nextTick等方法

<template>
  <div>{{ num }}</div>
</template>

<script>
import { rref, onMounted, onUpdated, onUnmounted,nextTick } from 'vue';
export default {
  name: 'base-form',
  setup() {
    const num = ref(0);
    onMounted(() => {
      console.log('mounted!');
    });
    onUpdated(() => {
      console.log('updated!');
    });
    onUnmounted(() => {
      console.log('unmounted!');
    });
    nextTick(() => {
      console.log('nextTick!');
    })
    return {
      num,
    };
  },
};
</script>

<style lang='less' scoped>
</style>

7.在組件內如何跳轉路由

<template>
  <div>
    <div @click="toPage">跳轉</div>
  </div>
</template>

<script>
import {} from 'vue';
import { useRouter } from 'vue-router';
export default {
  setup(props, { emit }) {
    const router = useRouter();
    const toPage = () => {
      router.push('/home');
    };
    return { toPage };
  },
};
</script>

<style lang='less' scoped>
</style>

更多新的不同會持續更新

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380