文章大部分的内容是看了技术胖的视频写的,有的也是贴的他的注解,感谢技术胖
安装vue-cli 须(v4.5.4以上版本),低版本自行更新
npm install -g @vue/cli
快速生成项目
vue create 项目名称
根据自己需要选择,如果想要ts,选择第三项
? Please pick a preset: (Use arrow keys) //请选择预选项
> Default ([Vue 2] babel, eslint) //使用Vue2默认模板进行创建
Default (Vue 3 Preview) ([Vue 3] babel, eslint) //使用Vue3默认模板进行创建
Manually select features //手动选择(自定义)的意思
空格键根据自己需要来进行选择,加上一个ts
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection)
>(*) Choose Vue version
(*) Babel
(*) TypeScript
( ) Progressive Web App (PWA) Support
( ) Router
( ) Vuex
( ) CSS Pre-processors //CSS预处理器
(*) Linter / Formatter //格式化工具
( ) Unit Testing //单元测试
( ) E2E Testing //E2E测试
选择3.x的版本
? Choose a version of Vue.js that you want to start the project with (Use arrow keys)
> 2.x
3.x (Preview)
我还不知道这个语法具体是干什么的,后面再补充吧,先选n
Use class-style component syntax?
是否使用TypeScript和Babel的形式编译 JSX.选n,没有写jsx什么的
Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? (Y/n)
eslint配置,先选第一项,有需要自己再配
? Pick a linter / formatter config: (Use arrow keys)
> ESLint with error prevention only
ESLint + Airbnb config
ESLint + Standard config
ESLint + Prettier
TSLint (deprecated)
lint的特性,有需要fix就选fix,自己配,我先选只校验
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)
>(*) Lint on save //保存的时候进行Lint
( ) Lint and fix on commit //需要帮你进行fix(修理),这项我们不进行选择
配置文件是否单独存放,我们选单独存放
Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
需不需要把这些配置保存下来,下次好直接进行使用。自己看情况选择,我选n
Save this as a preset for future projects? (y/N)
然后安装再起项目就可以了
npm run serve
|-node_modules -- 所有的项目依赖包都放在这个目录下
|-public -- 公共文件夹
---|favicon.ico -- 网站的显示图标
---|index.html -- 入口的html文件
|-src -- 源文件目录,编写的代码基本都在这个目录下
---|assets -- 放置静态文件的目录,比如logo.pn就放在这里
---|components -- Vue的组件文件,自定义的组件都会放到这
---|App.vue -- 根组件,这个在Vue2中也有
---|main.ts -- 入口文件,因为采用了TypeScript所以是ts结尾
---|shims-vue.d.ts -- 类文件(也叫定义文件),因为.vue结尾的文件在ts中不认可,所以要有定义文件
|-.browserslistrc -- 在不同前端工具之间公用目标浏览器和node版本的配置文件,作用是设置兼容性
|-.eslintrc.js -- Eslint的配置文件,不用作过多介绍
|-.gitignore -- 用来配置那些文件不归git管理
|-package.json -- 命令配置和包管理文件
|-README.md -- 项目的说明文件,使用markdown语法进行编写
|-tsconfig.json -- 关于TypoScript的配置文件
|-yarn.lock -- 使用yarn后自动生成的文件,由Yarn管理,安装yarn包时的重要信息存储到yarn.lock文件中,不使用就没有
setup 函数的用法,可以代替 Vue2 中的 date 和 methods 属性,直接把逻辑写在 setup 里就可以
使用setup()语法就不用写data了,一开始用不习惯,在setup里声明的变量,需要return才可以在页面中使用
<template>
<div>
<img alt="Vue logo" src="./assets/logo.png" />
<button v-for="(item, index) in boys" v-bind:key="index" @click="toSelectboy(index)">
{
{
index }} : {
{
item }}
</button>
<div>你选择踢【{
{
selectedBoy }}】一脚</div>
</div>
</template>
<script lang="ts">
import {
defineComponent,
ref
} from "vue";
export default defineComponent({
name: "App",
setup() {
const boys = ref(["渣虎", "渣辉", "渣茂"]);
const selectedBoy = ref("");
const toSelectboy = (index: number) => {
selectedBoy.value = boys.value[index];
};
return {
boys,
selectedBoy,
toSelectboy
};
},
components: {
},
});
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
在setup里声明的变量,想响应式需要用ref包裹一下,但是每次获取变量值的时候,并不能直接拿到这个值,需要.value,这样就很麻烦,我们来用reactive优化一下
reactive接受的参数是一个object,把要声明的变量都作为object的属性
<template>
<div>
<button v-for="(item, index) in data.boys" v-bind:key="index" @click="data.toSelectboy(index)">
{
{
index }} : {
{
item }}
</button>
<div>你选择踢【{
{
data.selectedBoy }}】一脚</div>
</div>
</template>
<script lang="ts">
import {
defineComponent,
reactive,
ref
} from "vue";
export default defineComponent({
name: "App",
setup() {
const data = reactive({
boys: ["渣虎", "渣辉", "渣茂"],
selectedBoy: "",
toSelectboy: (index: number) => {
data.selectedBoy = data.boys[index];
}
})
// const boys = ref(["渣虎", "渣辉", "渣茂"]);
// const selectedBoy = ref("");
// const toSelectboy = (index: number) => {
// selectedBoy.value = boys.value[index];
// };
return {
data
};
},
components: {
},
});
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
但是如果这样写的话,页面上每个变量前面都要加上data,所以我们再优化一下,(注意,返回时直接用拓展运算符去展开data是没有用的,那样返回的就是普通的变量,不能再具有响应式的能力)
<template>
<div>
<button v-for="(item, index) in boys" v-bind:key="index" @click="toSelectboy(index)">
{
{
index }} : {
{
item }}
</button>
<div>你选择踢【{
{
selectedBoy }}】一脚</div>
</div>
</template>
<script lang="ts">
import {
defineComponent,
reactive,
ref,
toRefs
} from "vue";
export default defineComponent({
name: "App",
setup() {
const data = reactive({
boys: ["渣虎", "渣辉", "渣茂"],
selectedBoy: "",
toSelectboy: (index: number) => {
data.selectedBoy = data.boys[index];
}
})
const refData = toRefs(data);
return {
...refData
};
},
components: {
},
});
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
这样就可以再页面上直接用变量了,我们再优化一下,加上类型注解
<template>
<div>
<button v-for="(item, index) in boys" v-bind:key="index" @click="toSelectboy(index)">
{
{
index }} : {
{
item }}
</button>
<div>你选择踢【{
{
selectedBoy }}】一脚</div>
</div>
</template>
<script lang="ts">
import {
defineComponent,
reactive,
toRefs
} from "vue";
interface DataProps {
boys: string[];
selectedBoy: string;
toSelectboy: (index: number) => void;
}
export default defineComponent({
name: "App",
setup() {
const data: DataProps = reactive({
boys: ["渣虎", "渣辉", "渣茂"],
selectedBoy: "",
toSelectboy: (index: number) => {
data.selectedBoy = data.boys[index];
}
})
const refData = toRefs(data);
return {
...refData
};
},
components: {
},
});
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
注意:生命周期函数不要用箭头函数(箭头函数没有自己的this,所以可能会报错)
setup() :开始创建组件之前,在beforeCreate和created之前执行。创建的是data和method
onBeforeMount() : 组件挂载到节点上之前执行的函数。
onMounted() : 组件挂载完成后执行的函数。
onBeforeUpdate(): 组件更新之前执行的函数。
onUpdated(): 组件更新完成之后执行的函数。
onBeforeUnmount(): 组件卸载之前执行的函数。
onUnmounted(): 组件卸载完成后执行的函数
onActivated(): 没用过,技术胖说是被包含在中的组件,会多出两个生命周期钩子函数。被激活时执行。
onDeactivated(): 比如从 A 组件,切换到 B 组件,A 组件消失时执行。
onErrorCaptured(): 当捕获一个来自子孙组件的异常时激活钩子函数(还没用过)。
const app = {
name: "App",
setup() {
console.log("1-开始创建组件-----setup()");
const data: DataProps = reactive({
boys: ["渣虎", "渣辉", "渣茂"],
selectedBoy: "",
toSelectboy: (index: number) => {
data.selectedBoy = data.boys[index];
}
})
onBeforeMount(() => {
console.log("2-组件挂载到页面之前执行-----onBeforeMount()");
});
onMounted(() => {
console.log("3-组件挂载到页面之后执行-----onMounted()");
});
onBeforeUpdate(() => {
console.log("4-组件更新之前-----onBeforeUpdate()");
});
onUpdated(() => {
console.log("5-组件更新之后-----onUpdated()");
});
const refData = toRefs(data);
return {
...refData,
};
},
};
export default app;
vue2的钩子函数还是可以继续用的
看一下vue2和vue3的对比
Vue2--------------vue3
beforeCreate -> setup()
created -> setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
activated -> onActivated
deactivated -> onDeactivated
errorCaptured -> onErrorCaptured
onRenderTracked直译过来就是状态跟踪,它会跟踪页面上所有响应式变量和方法的状态,也就是我们用return返回去的值,他都会跟踪。只要页面有update的情况,他就会跟踪,然后生成一个event对象,我们通过event对象来查找程序的问题所在。
引用然后再setup里使用
import {
.... ,onRenderTracked,} from "vue";
onRenderTracked((event) => {
console.log("状态跟踪组件----------->");
console.log(event);
});
onRenderTriggered直译过来是状态触发,它不会跟踪每一个值,而是给你变化值的信息,并且新值和旧值都会给你明确的展示出来。
使用和onRenderTracked一致,event的属性如下
- key 哪个变量发生了变化
- newValue 更新后变量的值
- oldValue 更新前变量的值
- target 目前页面中的响应变量和函数
引入watch
import {
... , watch} from "vue"
在setup里实现watch
watch(要监听的变量, (newValue, oldValue) => {
要做的事
});
如果监听reactive变量data里的属性,需要用函数返回的形式,否则会报错
watch([变量1, () => data.变量], (newValue, oldValue) => {
console.log(`new--->${
newValue}`);
console.log(`old--->${
oldValue}`);
});
监听多个变量,拿到的回调的值也是一个数组
模块化ts文件一般放在hooks目录下
新建一个useNowTime.ts文件
import {
ref} from "vue";
const nowTime = ref('00:00:00')
const getTime = () => {
const now = new Date();
const hour = now.getHours() < 10 ? "0" + now.getHours() : now.getHours()
const minu = now.getMinutes() < 10 ? "0" + now.getMinutes() : now.getMinutes()
const sec = now.getSeconds() < 10 ? "0" + now.getSeconds() : now.getSeconds()
nowTime.value = hour + ":" + minu + ":" + sec
console.log(nowTime.value);
}
export {
nowTime,
getTime
}
在需要用到的地方引入
import {
nowTime, getNowTime } from "./hooks/useNowTime";
记得在setup里一定要return出去,页面才能用
return {
nowTime,getNowTime};
当然,模块化里可以封装好暴露一个方法,比如调一个api,然后拿到这个方法的返回值,这里是用的技术胖的链接,哈哈,感谢技术胖
import {
ref } from "vue"
import axios from "axios"
function useAxiosUrl(url: string) {
const result = ref(null)
const loading = ref(true)
const loaded = ref(false)
const error = ref(null)
axios.get(url).then( (res) => {
loading.value = false
loaded.value = true
result.value = res.data
}).catch(e => {
console.log(e);
loading.value = false
})
return {
result ,
loading,
loaded ,
error
}
}
export default useAxiosUrl
页面引入
import useAxiosUrl from "./hooks/useUrlAxios"
使用
const {
result,
loading,
loaded
} = useAxiosUrl("https://apiblog.jspang.com/default/getGirl")
记得return
return {
result,
loading,
loaded
};
生成路由
// vue2
const router = new Router({
mode: history,
...
});
// vue3
import {
createRouter, createWebHistory} from 'vue-next-router';
const router = createRouter({
history: createWebHistory(),
...
})
base,vue3里传给createWebHistory()的第一个参数为base
// vue2
const router = new Router({
base: __dirname,
...
})
// vue3
import {
createRouter, createWebHistory} from 'vue-next-router';
const router = createRouter({
history: createWebHistory(/),
...
})
// 导航守卫----vue2
import router from ‘./router‘
router.beforeEach((to, from, next) => {
// ...
if (to.matched.length === 0) {
next('/404')
} else {
next()
}
//console.log(to, from, next, '路由守卫')
})
//导航守卫----vue3,在app.vue里
import {
useRouter
} from 'vue-router';
export default {
name: 'App',
setup() {
const router = useRouter();
router.beforeEach((to, from, next) => {
// ...
if (to.matched.length === 0) {
next('/404')
} else {
next()
}
})
}
}
vue2里到一个不存在的路由,除非配置了404,否则会到根页面,而vue3push或者resolve一个不存在的命名路由时,会引发错误,不会导航到根路由,页面空白
还没有看,后面再写