vue3+typescript入门到精通

vue3.0入门到精通

附B站讲解视频

vue3+typescript入门到精通

vue3.0 安装

前安装过vue的2.0版本,你需要把2.0相关的删除

npm uni -g vue-cli

安装vue/cli脚架

npm i -g @vue/cli

检查版本号,目前安装vuecli 4.5.4

vue -V

创建:在命令窗口输入指令

选择default vue 3

vue create 项目名称

vue composition API


vue3.0 侧重于解决代码组织与逻辑复用问题

目前,我们使用的是“options”API 构建组件。 为了将逻辑添加到Vue组件中,我们填充(options)属性,如data、methods、computed等。 这种方法最大的缺点是,它本身不是一个工作的JavaScript代码。 您需要确切地知道模板中可以访问哪些属性以及this关键字的行为。在底层,Vue编译器需要将此属性转换为工作代码。正因为如此,我们无法从自动建议或类型检查中获益。

Composition API希望将通过当前组件属性、可用的机制公开为JavaScript函数来解决这个问题。 Vue核心团队将组件Composition API描述为“一套附加的、基于函数的api,允许灵活地组合组件逻辑”。 使用Composition API编写的代码更易读,并且场景不复杂,这使得阅读和学习变得更容易。

让我们看到一个非常简单的组件示例,它使用新的组件Composition API来理解它是如何工作的。

<template>

  <div id="app">

   <img alt="Vue logo" src="./assets/logo.png">

   <div>{{msg}}年龄为{{age}}</div>

   <button @click="add"> + </button>

  </div>

</template>

<script>

export default {

  name: 'App',

  data() {

   return {

     msg:'王大合',

     age:18

   }

  },

  methods:{

   add() {

     this.age += 1

   }

  }

}

</script>

setup

vue3.0将组件的逻辑都写在了函数内部,setup()会取代vue2.x的data()函数,返回一个对象,暴露给模板,而且只在初始化的时候调用一次,因为值可以被跟踪,所以我们通过vue3来改变编程习惯

首先引入ref

使用数据需要return

import {ref} from 'vue'

setup() {

   const msg = ref('王大合')

   const age = ref(18)

   function add() {

     age.value +=1

   }

   return {msg,age,add}

  }

computed

<template>

  <div id="app">

   <img alt="Vue logo" src="./assets/logo.png">

   <div>{{msg}}的年龄为{{age}}</div>

   <div>{{double}}</div>

   <button @click="add">+</button>

  </div>

</template>

<script>

import {computed, ref} from 'vue'

export default {

  name: 'App',

  setup() {

   const msg = ref('王大合')

   const age = ref(18)

   const double = computed(() =>{

     return age.value * 2

   })

   function add() {

     age.value += 1

   }

   return {msg,age,add,double}


  }

}

</script>

reactive

在 setup 函数里面, 我们适应了 Vue3.0 的第一个新接口 reactive 它主要是处理你的对象让它经过 Proxy 的加工变为一个响应式的对象,

toRefs

用于将响应式对象变成普通对象

<template>

  <div id="app">

   <img alt="Vue logo" src="./assets/logo.png">

   <div>{{msg}}的年龄为{{age}}</div>

   <div>{{double}}</div>

   <button @click="add">+</button>

  </div>

</template>

<script>

import {computed, reactive,toRefs} from 'vue'

export default {

  name: 'App',

  setup() {

   const state = reactive({

       msg:'王大合',

       age:18,

       double : computed(() =>{

     return state.age * 2

   })

   })

   function add() {

     state.age += 1

   }

   return {...toRefs(state),add}


  }

}

</script>

props 和 context

在 Vue2.0 中我们可以使用 props 属性值完成父子通信,在这里我们需要定义 props 属性去定义接受值的类型,然后我们可以利用 setup 的第一个参数获取 props 使用。

export default {

  name: 'App',

  components:{

   Content

  },

  setup() {

   const state = reactive({

       msg:'王大合',

       age:18,

       double : computed(() =>{

     return state.age * 2

   })

   })

   function add() {

     state.age += 1

   }

   return {...toRefs(state),add}


  }

}

我们在 App.vue 里面就可以使用该头部组件,有了上面的 props 我们可以根据传进来的值,让这个头部组件呈现不同的状态。

<template>

  <div id="app">

   <img alt="Vue logo" src="./assets/logo.png">

   <div>{{msg}}的年龄为{{age}}</div>

   <div>{{double}}</div>

   <Content :msg='msg' />

   <button @click="add">+</button>

  </div>

</template>

这里我新建一个新的组件content,在app.vue中引入

<!--  -->

<template>

  <div>{{data}}</div>

</template>

<script>

import {ref} from 'vue'

export default {

name:'content',

props:{

    msg:String

},

setup(props) {

    const data = ref(props.msg)

    return {data}

}

}

</script>

setup 函数的第二个参数是一个上下文对象,这个上下文对象中包含了一些有用的属性,这些属性在 Vue2.0 中需要通过 this 才能访问到,在 vue3.0 中,访问他们变成以下形式:

setup(props, ctx) {

  console.log(ctx) // 在 setup() 函数中无法访问到 this

  console.log(this) // undefined

}

具体能访问到以下有用的属性:

- slot

- attrs

- emit

父组件

<template>

  <div id="app">

   <img alt="Vue logo" src="./assets/logo.png">

   <div>{{msg}}的年龄为{{age}}</div>

   <div>{{double}}</div>

   <Content :msg='msg' @change='showName' />

   <button @click="add">+</button>

  </div>

</template>

<script>

import {computed, reactive,toRefs} from 'vue'

import Content from './components/content.vue'

export default {

  name: 'App',

  components:{

   Content

  },


  setup() {

   const state = reactive({

       msg:'王大合',

       age:18,

       double : computed(() =>{

         return state.age * 2

       })

   })

   function showName(params) {

       alert(params)

   }

   function add() {

     state.age += 1

   }

   return {...toRefs(state),add,showName}


  }

}

</script>

子组件

<!--  -->

<template>

  <div>{{data}}</div>

  <button @click="changeName">打个招呼!</button>

</template>

<script>

import {ref} from 'vue'

export default {

name:'content',

props:{

    msg:String

},

setup(props,context) {

    const data = ref(props.msg)

   function changeName() {

       context.emit('change','Hello,王大合!')

   }

    return {data,changeName}

}

}

</script>

watch

监听ref

不指定数据源

const a = ref(18)

watch(()=>{

    console.log(a.value)

})

指定数据源

const a = ref(18)

  watch(a,()=> {

  console.log(a.value)

  })

监听reactive

const state = reactive({

       msg:'王大合',

       age:18,

       double : computed(() =>{

         return state.age * 2

       })

   })

不指定数据源

watch(()=>{

    console.log(state.age)

})

指定数据源

watch(()=>state.age,()=>{

    console.log(state.age)

})

回调函数参数以及watche clean,使用clean时候是处理重复性的watch监听事件

watch(() => state.age,(newVal,oldVal,clean)=> {

    console.log(state.msg + "去年年纪:"+oldVal +"今年年纪:" + newVal)

    clean(

      ()=>{

        console.log('clean')

      }

    )

  })

vue3.X+vite+typescript

放弃webpack,使用vite安装vue3.0

这个是尤大开发的新工具,目的是以后替代webpack,原理是利用浏览器现在已经支持es6的import了,遇到import会发送一个http请求去加载文件,vite拦截这些请求,做一些预编译,省去了webpack冗长打包的时间,提升开发体验

npm install -g create-vite-app

create-vite-app vue3-vite

cd vue3-vite

npm install

npm run dev

# 或者使用yarn

yarn add -g create-vite-app

yarn create vite-app <project-name>

安装依赖

yarn

使用yarn启动项目

yarn dev

引入typescript

# 安装 typescript

yarn add typescript -D

初始化tsconfig.json

# 然后在控制台执行下面命令

npx tsc --init

将main.js修改为main.ts,同时将index.html里面的引用也修改为main.ts,

然后在script 里添加 lang="ts"

<template>

  <img alt="Vue logo" src="./assets/logo.png" />

  <HelloWorld msg="Hello Vue 3.0 + Vite" />

</template>

<script lang="ts">

import HelloWorld from './components/HelloWorld.vue'

export default {

  name: 'App',

  components: {

   HelloWorld

  }

}

</script>

修改完之后,重启就可以访问项目了。虽然这样配置是可以了,但是打开main.ts会发现import App from App.vue会报错:Cannot find module './App.vue' or its corresponding type declarations.,这是因为现在ts还没有识别vue文件,需要进行下面的配置:

在项目根目录添加shim.d.ts文件

# powerShell终端,也可以手动创建

New-Item shim.d.ts

添加以下内容

declare module "*.vue" {

  import { Component } from "vue";

  const component: Component;

  export default component;

}

安装vue-router

yarn add vue-router@4.0

这样可以选择最新的vue-router 4.0.0的测试版本,这里更新到beta.13

配置vue-router

在项目src目录下面新建router目录,然后添加index.ts文件,在文件中添加以下内容

import {createRouter, createWebHashHistory} from 'vue-router'

// 在 Vue-router新版本中,需要使用createRouter来创建路由

export default createRouter({

  // 指定路由的模式,此处使用的是hash模式

  history: createWebHashHistory(),

  // 路由地址

  routes: []

})

安装vuex

同上

yarn add vuex@4.0

目前只能选择最新测试版

在项目src目录下面新建store目录,并添加index.ts文件,文件中添加以下内容

import { createStore } from 'vuex'

interface State {

  userName: string

}

export default createStore({

  state:{

  userName:'王大合'

  }

});

main.ts中引入vuex和vue-router

import { createApp } from 'vue'

import App from './App.vue'

import './index.css'

import router from './router/index'

import vuex from './store/index'

const  app = createApp(App)

app.use(router)

app.use(vuex)

app.mount('#app')

上线小项目todoList

app.vue

<template>

  <div id="app">

   <div id="nav">

     <router-link to="/">todoList</router-link> |

     <router-link to="/about">About</router-link>

   </div>

   <router-view/>

  </div>

</template>

<script lang="ts">

export default {

  name: 'App'

}

</script>

<style lang="scss">

  #app {

  font-family: Avenir, Helvetica, Arial, sans-serif;

  -webkit-font-smoothing: antialiased;

  -moz-osx-font-smoothing: grayscale;

  text-align: center;

  color: #2c3e50;

}

#nav {

  padding: 30px;

  a {

   font-weight: bold;

   color: #2c3e50;

   &.router-link-exact-active {

     color: #42b983;

   }

  }

}

</style>

配置路由 router/index.ts

import {createRouter, createWebHashHistory} from 'vue-router'

// 在 Vue-router新版本中,需要使用createRouter来创建路由

export default createRouter({

  // 指定路由的模式,此处使用的是hash模式

  history: createWebHashHistory(),

  // 路由地址

  routes: [

   {

   path: '/',

   // 必须添加.vue后缀

   component: () => import('../views/todo-list.vue')

     },

   {

       path: '/about',

       name: 'About',


       component: () => import('../views/About.vue')

     }

]

})

store的index.ts新建vuex

import { createStore } from 'vuex'

interface State {

  userName: string

  taskList: any[]


}

export default createStore({

  state: {


     userName: "王大合",

     taskList: []


  },

  mutations:{


   createTask (state:any, newTask:string) {

       state.taskList.push(newTask)

     },

     deleteTask (state:any, index:number) {

       state.taskList.splice(index, 1)

     },

     updateStatus (state:any, payload:any) {

       const { index, status } = payload


       state.taskList[index].isfinished = status

     }

  }

});

在src目录新建view文件夹,创建todoList和about

todoList

<template>

  <div class="home">

   <!-- input输入list内容 -->

   <div>

      <input

   @keyup.enter="addTask"

     class="input"

     type="text"

     v-model="inputValue"

     placeholder="请输入" />

   </div>

   <!-- todoList内容展示和删除 -->

    <ul class="ul">

     <li class="item" v-for="(item, index) in taskList" :key="index">

       <p

       @click="updateStatus(index, !item.isfinished)"

       class="content"

       :class="item.isfinished ? 'active' : ''"

       >{{item.lable}}</p>

       <div class="item-delete" @click="deleteTask(index)">X</div>

     </li>

     <li v-if="taskList.length === 0" class="item-none">暂无数据</li>

   </ul>

  </div>

</template>

<script lang="ts">

import { ref, computed } from 'vue';

import { useStore } from "vuex";

export default {


  setup() {

   const store = useStore()

   const taskList = computed(() => store.state.taskList);

   const inputValue = ref('');

   const addTask = () => {

     store.commit('createTask', {

       lable: inputValue.value,

       isfinished: false

     })

     inputValue.value = ''

   }

   const updateStatus = (index, status) => {

     store.commit('updateStatus', {

       index,

       status

     })

   }

   const deleteTask = (index) => {

     store.commit('deleteTask', index)

   }

   return {

     inputValue,

     taskList,

     addTask,

     updateStatus,

     deleteTask

   };

  }

}

</script>

<style scoped lang='scss'>

* {

  box-sizing: border-box;

  margin: 0;

  padding: 0;

}

ul,

li {

  list-style: none;

  text-align: left;

}

.home {

  max-width: 400px;

  margin: 0 auto;

  .input {

   width: 100%;

   height: 40px;

   border-radius: 5px;

   outline-style: none;

   border: 2px solid #999;

   padding: 5px 10px;

  }

  .ul {

   margin-top: 10px;

  }

  .item {

   height: 40px;

   line-height: 40px;

   padding-bottom: 5px;

   border-bottom: 1px solid #dcdfe6;

   color: #333333;

  }

  .item-none {

   height: 40px;

   line-height: 40px;

   padding-bottom: 5px;

   color: #333333;

   text-align: center;

  }

  .content {

   float: left;

   height: 40px;

   line-height: 40px;

   cursor: pointer;

  }

  p.active {

   text-decoration:line-through;

   color: #999999;

  }

  .item-delete {

   float: right;

   width: 25px;

   text-align: center;

   cursor: pointer;

  }

}

</style>

about

<template>

  <div class="about">

   <h1>{{name}}</h1>



  </div>

</template>

<script lang='ts'>

import { ref, watch } from 'vue';

export default {

  name: 'about',

  setup() {

   const name = ref('王大合出品');



   return {

     name,


   };

  }

};

</script>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345