vue3.0+ts小练习

445 阅读4分钟

写作时间2020-12月份

脚手架安装

vue提供了脚手架可以直接生成开发环境,可以全局安装vue-cli脚手架,安装命令

npm install yarn -g
//这里建议用 yarn 安装,npm 安装没有外网会装不上
yarn global add @vue/cli
//此处安装新版本,且安装到全局

vue -V // 查看 vue/cli 版本 此处是 4.3.1 版本

创建新项目

vue create project-vue

此处有一个选择,第一个 defalut 是默认的不包含 ts 编译支持的,需要选择 Manually select features 即手动选择添加 ts 支持

添加需要其他一些功能支持,此处上下按钮切换到对应栏,按空格添加活删除所需功能,此处选择 ts vuex router css 功能,然后 enter 下一步 是否使用 class 风格装饰器,原本 vue2 创建 class 是 class Demo extends Vue{}, 到了 vue3 可以不用这中方法,使用 Dome = new Vue() 即可,所以这里可以不选

  • Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX) 是否需要支持 jsx,这里不需要,
  • Pick a linter / formatter config 选择Linter / Formatter 代码规范类型,选择 standard 规范即可 规范地址
  • Pick additional lint features: 语法检查方式,这里选择保存就检查
  • Where do you prefer placing config for Babel, ESLint, etc. babel,eslint文件放在那里,
  • Save this as a preset for future projects 将此设置保存为以后的通用设置,设为 n
  • Pick the package manager to use when installing dependencies 包管理器用啥安装,这里选择yarn,

也可以使用另外一种方式创建项目控制台输入 vue ui 可以打开本地 8000 端口 localhost:8000 可以按照上面配置创建项目

项目目录

生命周期对比

beforeCreate -> use seup() created -> use setup() beforeMount -> onBeforeMount mounted -> onMounted beforeUpdate -> onBeforeUpdate updated -> onUpdated beforeDestory -> onBeforeUnmount destoryed -> onUnmounted activated -> onActivated deactivated -> onDeactivated errorCaptured -> onErrorCaptured 新增 onRenderTracked onRenderTriggered // 数据变化渲染的时候监听

数据响应demo (包含ref,computed)

  • setUp 钩子函数,在 beforeCreate 创建之前使用,返回的数据在模板中直接使用,其中没有 this 属性,
  • ref 可以监听基本数据类型的变化然后触发模板重新渲染,其以 value 值来访问数据
<template>
  <div>
    <div>{{age}}</div>
    <div>{{three}}</div>
    <button @click="add">加+1</button>
  </div>
</template>

<script lang="ts">
import { computed, ref } from 'vue'
export default {
  name: 'App',
  setup () {
    const name = ref('xx')
    const age = ref(0)
    const add = () => {
      age.value++
    }
    const three = computed(() => {
      return age.value * 3
    })
    return {
      name,
      age,
      add,
      three
    }
  }
}
</script>

点击 加+1 之后 age 就会跟着 +1

为了让数据放在一起更好看,可以用 reactive 创建数据,他会把数据,和操作方法包裹起来,使其看起来更整体

<template>
  <div>
    <div>{{data.age}}</div>
    <div>{{data.three}}</div>
    <button @click="data.add">加+1</button>
  </div>
</template>

<script lang="ts">
import { computed, ref, reactive } from 'vue'
interface DataType {
  age: number;
  add: () => void;
  three: number;
}
export default {
  name: 'App',
  setup () {
  // 代码更整洁了
    const data: DataType = reactive({
      age: 0,
      add: () => { data.age++ },
      three: computed(() => data.age * 3)
    })
    return {
      data
    }
  }
}
</script>

如果想直接获取 age, add, three 不用 data.xx 访问的话可以用 toRefs toRefs 函数可以将 reactive 创建出来的响应式对象转换为普通对象,这个对象上的每个属性节点,都是 ref 类型的响应式数据

<template>
  <div>
    <div>{{age}}</div>
    <div>{{three}}</div>
    <button @click="add">加+1</button>
  </div>
</template>

<script lang="ts">
import { computed, ref, reactive, toRefs } from 'vue'
interface DataType {
  age: number;
  add: () => void;
  three: number;
}
export default {
  name: 'App',
  setup () {
    const data: DataType = reactive({
      age: 0,
      add: () => { data.age++ },
      three: computed(() => data.age * 3)
    })
    const refData = toRefs(data)
    return {
      ...refData
    }
  }
}
</script>

watch 用法

<template>
  <div>
    <div>{{age}}</div>
    <div>{{three}}</div>
    <div>{{arr[0]}}</div>
    <div>{{title}}</div>
    <button @click="add">加+1</button>
    <button @click="updateTitle">改变title</button>
  </div>
</template>

<script lang="ts">
import { computed, ref, reactive, toRefs, watch } from 'vue'
interface DataType {
  age: number;
  add: () => void;
  three: number;
  arr: [number];
}
export default {
  name: 'App',
  setup () {
    const data: DataType = reactive({
      age: 0,
      add: () => { data.age++ },
      three: computed(() => data.age * 3),
      arr: [0]
    })
    const title = ref('')
    const updateTitle = () => {
      title.value = 'xx'
    }
    watch(title, () => {
      document.title = title.value
    })
    const refData = toRefs(data)
    return {
      ...refData,
      updateTitle,
      title
    }
  }
}
</script>

watch 也可以监听多个数据

watch([title, data], () => {
  console.log(data)
  document.title = title.value + '' + data.age
})
// 也可以函数形式只监听 data.age
watch([title, () => data.age], (oldVal, newVal) => {
      console.log(oldVal, newVal)
      document.title = title.value + '' + data.age
    })

watchEffect

watchEffect 返回一个函数,函数里面可以直接只监听用到的字段的变化,其他字段不会触发watch

export default defineComponent({
  name: "App",
  setup() {
    const name = ref("name:");
    const age = ref(123);
    setInterval(() => {
      name.value += "1";
    }, 2000)
    setInterval(() => {
      age.value += 3
    }, 5000)
    watchEffect(() => {
      console.log("只打印age",age.value);
    })
    return {
      name,
      age
    }
  },
});
/*
只打印age 123
只打印age 126
只打印age 129
只打印age 132
只打印age 135
*/

onMounted, onUnmounted

以绑定点击获取鼠标点击位置事件来展示 onMounted, onUnmounted 的使用

<script lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
export default {
  name: 'App',
  setup () {
    const x = ref(0)
    const y = ref(0)
    const updateMouse = (e: MouseEvent) => {
      x.value = e.pageX
      y.value = e.pageY
    }
    onMounted(() => {
      document.addEventListener('click', updateMouse)
    })
    onUnmounted(() => {
      document.removeEventListener('click', updateMouse)
    })
    return {
      x, y
    }
  }
}
</script>

这里可以把 移动 获取事件提取出一个 hooks 函数

import { ref, onMounted, onUnmounted } from 'vue'
function mousePostion () {
  const x = ref(0)
  const y = ref(0)
  const updateMouse = (e: MouseEvent) => {
    x.value = e.pageX
    y.value = e.pageY
  }
  onMounted(() => {
    document.addEventListener('click', updateMouse)
  })
  onUnmounted(() => {
    document.removeEventListener('click', updateMouse)
  })
  // 组件返回坐标 x,y
  return {
    x, y
  }
}
export default mousePostion
// 引用上面的函数 直接使用
import mousePostion from './commonHooks/mousePostion'
export default {
  name: 'App',
  setup () {
  // 调用时可直接获取值
    const { x, y } = mousePostion()
    return {
      x, y
    }
  }
}
</script>

h 函数

vue中h函数是创建一个虚拟节点,然后插入节点。

import { createApp, defineComponent, h,ref } from "vue";
import store from "./store";
const App = defineComponent({
  setup() {
    const data = ref(1)
    setInterval(() => {
      data.value += 2
    }, 2000)
    // const renderData = data.value // 这里是setup函数只会触发一次,setup函数里面 return 的数据变化后函数会被多次调用,重新渲染,
    return () => {
    	const renderData = data.value // 如果想重新定义简写数据,只有在这里定义数据,dom才会触发渲染
      return h("div", { id: "app" }, [
        h("div", renderData)
      ])
    }
  } 
})
createApp(App)
  .use(store)
  .mount("#app");

telport

有时候像 Modal 框,Message 消息栏,我们希望是放在全局,跟 app 同级的地方使用的,

modal 组件,

<template>
  <div class="modal">
      modal
  </div>
</template>

<script>
export default {

}
</script>

<style>
    .modal{
        padding: 20px;
        border: 1px solid #ccc;
    }
</style>

一般调用方法调用

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

<script lang="ts">
import modal from './components/modal.vue'
export default {
  name: 'App',
  components: {
    modal
  }
}
</script>

modal 组件是处在 app 目录下的


使用teleport方法, 首先在 public/index.html 下面在 app 同级 添加一个 modal div, teleport 将要放到的地方 在对 modal 组件进行修改

<template>
<teleport to="#modal"> // 指定组件放到 modal div 上
  <div class="modal">
      modal
  </div>
</teleport>
</template>

结果

事件传递 emit

<template>
  <teleport to="#modal">
    <div v-show="isOpen">
      <div class="modal" v-show="isOpen">modal</div>
      <button @click="buttonClick">modal事件</button>
    </div>
  </teleport>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
  props: {
    isOpen: Boolean
  },
  emits: {
    close: (load: any) => {  // 父级绑定的事件 @close 用法
      return load.type === 'hello' // 这里返回数据判断,是否符合需要的数据,如果错误了,控制台会报警告,
    }
  },
  setup(props, context) {
    const buttonClick = () => {
      context.emit('close', {  // 设定一个子组件绑定的事件,及其传递的数据,
        type: 'hello'
      })
    }
    return {
      buttonClick
    }
  }
})
</script>
<template>
  <div>
    <div></div>
    <modal :isOpen="modalIsOpen" @close="closeModal" />
    <div @click="showModal">openModal</div>
  </div>
</template>

<script lang="ts">
import { ref } from 'vue'
import modal from './components/modal.vue'
export default {
  name: 'App',
  components: {
    modal
  },
  setup() {
    const modalIsOpen = ref(false)
    const showModal = () => {
      modalIsOpen.value = true
    }
    const closeModal = (rs: any) => {
      console.log(rs)
      modalIsOpen.value = false
    }
    return {
      modalIsOpen,
      showModal,
      closeModal
    }
  }
}
</script>

Suspense 异步组件

suspense 可以加载一组异步组件,用法

<template>
  <h1>{{result}}</h1>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
    setup() {
    	// 返回一个 promise
        return new Promise((resolve) => {
            setTimeout(() => {
                return resolve({ // 最后返回 result 
                    result: '成功'
                })
            }, 3000) // 3s 后返回
        })
    }
})
</script>
<template>
  <div>
    <Suspense> // 使用 suspense 包裹起来
      <template #default> // #default 是默认需要加载的异步组件
        <async-tel></async-tel>
      </template>
      <template #fallback> // 当异步组件没有返回的时候,加载一个    
       loading
        <div>加载中...</div>
      </template>
    </Suspense>
  </div>
</template>

<script lang="ts">
import AsyncTel from './components/AsyncTel.vue' // 引起组件
export default {
  name: 'App',
  components: {
    AsyncTel
  }
}
</script>

在 vscode 中 设置 vetur 属性,会把 ts 类解析到 template 模板中, vetur.experimental.templateInterpolationService = true