TypeScript
2020年了,身为前端的我们来说,要承认的一个事实就是typescript是越来越火了。
可以看看NPM官方的周下载量
达到了1000+万次的周下载量
这个图可以告诉我们确实,将来的前端主流可能要向TypeScript偏移了,因为免不了的就是规范。
那么说到TypeScript,那么他到底是什么呢?他到底怎么去使用呢?
其实众所周知 javascript是一个弱类型语言,举一个最简单的比方我们在使用js声明一个变量时,是不需要去声明变量类型的
var a = 10 // 我声明了一个变量
有好处吗?当然有,省事
有坏处吗?当然有,类型不明显
那么TypeScript是什么呢?
其实就是用来规划js语言的,将js的写法用强类型的写法去写
我们可以看看typescript声明变量
var a:Number = 10 //我声明了一个Number类型的变量
其实并没有什么差别,就是我在变量后指定了它的类型
就是要告诉他我声明了一个number类型的变量
简单的了解了TypeScript之后,我们就可以直接去看一下怎么样使用typescript
在Vue中使用typeScript
首先我这边技术栈是使用了Vue,当然像React也是可以使用typescript的,而Angular规范语言就是ts,所以我们在这里就先来介绍在Vue中如何去使用ts
首先免不了的还是使用我们的VueCli来创建项目,而现在其实vue3也快出来了,当然大家也要抓紧时间去学习vue3呀。
我们可以看到在我们使用
vue create ts
页面呈现效果,其实这个时候我们可以直接勾选typescript
我们也可以不勾选,在内部手动进行安装,在这里的话我们就先不进行勾选了,等项目创建完成之后我们可以进行手动安装ts
当项目创建完成之后
我们进入到项目当中
这是我们的项目结构,我们这个时候可以使用
vue add @vue/typescript
将js+vue项目 转成 ts+vue项目
这个时候会让我们选择,就一直Y就可以了
我们使用npm run serve启动项目之后
呈现这个窗口也就成功了
项目呈现效果
其实也就是所谓的增删改查
那么上干货走起
其实我们的增删改查也没有用到后台的数据接口,只是说单纯的使用到了localstorage来进行一系列的增删改查
第一步
安装vuex
npm install vuex -S
在src下创建store/index.ts文件 注意是ts文件
store/index.ts
import Vue from 'vue' // 引入vue
import Vuex from 'vuex' // 引入vuex
Vue.use(Vuex) // 挂载vuex
const store = new Vuex.Store({
state: {
menuList: ''
},
mutations: {
}
})
// 导出store,我们在main.ts中引入store
export default store
src/main.ts
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
new Vue({
store, // 挂载store
render: h => h(App)
}).$mount('#app')
第二步
我们新建一个类文件,专门用于存储一系列的类方法
我这边在src文件夹下新建了一个model文件专门用来存储
model/CurdMethods.ts
// 定义接口
interface All {
isCate: number,
id: number,
title: string,
content: string,
[propName: string]: any
}
// 定义接口 接口是什么? 其实接口就是用来规范定义对象类型的 可以简单的将其理解为一个 String 只不过这个规则String是我们自己来定义的
interface IClass {
MenuList:any // 表示任意类型 一个any走遍TypeScript 其实我们是不到万不得已不推荐使用的
isCate:number
id:number
title:string
content:string
Read():object // 表示return一个对象
Add(objCan:any):void // void 表示无返回值类型 函数后添加 :类型 表示return 的类型
Edit(objCan:any):void
Del(objCan:any):void
smallTianCai?:string // 可以定义也可一部定义
[propName: string]: any //接口任意属性
}
class CurdMethods implements IClass {
// 所有文章列表
MenuList!: any // !: 表示这个对象我们在初始时可以不给他赋值 只是单纯的声明,如果不加 ! 的话ts会给我们报错
// 类别分类
isCate !: number
// id
id!: number
// 标题
title !: string
// 内容
content !: string
constructor(isCate: number = 0, id: number = -1, title: string = '', content: string = '') {
this.id = id
this.isCate = isCate
this.title = title
this.content = content
// 实例化的时候就去执行获取数据
this.MenuList = this.Read()
}
// 查询方法
Read(): object {
// 从localStorge中获取数据 :string | null 代表的意思是 这个Menu的值可以是字符串也可以是null 因为获取的数据可能是字符串也可能没有就是null
let strData: string | null = localStorage.getItem('Menu')
// 声明一个变量 指定类型在这里我们给其定义为一个any类型 值为 [ ]
let Menu: any = []
// 如果strData不是 null的话 就说明有数据 有数据我们就给null赋值
if (strData != null) {
// JSON.parse 转译为数组对象
Menu = JSON.parse(strData)
}
// 将这个数组对象赋值给 this.MenuList
this.MenuList = Menu
// 我们将这个查出的数据 同样return 出去 以方便别的类方法中调用
return Menu
}
// 增加方法
Add(objCan:any): void {
// 获取this.menuList的数据,将数据插入重新插入到localstorage中
const obj: All = {
id: new Date().getTime(),
isCate: Number(objCan.isCate),
title: objCan.title,
content: objCan.content
}
// push
this.MenuList.push(obj)
// 存入localstorage
localStorage.setItem('Menu', JSON.stringify(this.MenuList))
// 因为怎么说呢,vue2对push方法的监听是不能及时监听到的,实现不了响应式,所以我们在这里需要重新调用一边this.Read方法对this.MenuList进行重新赋值
this.Read()
}
// 修改方法
Edit(objCan:any): void {
// 存入localstorage
let obj: any = this.MenuList.filter((item: any) => {
return item.id === objCan.id
})[0]
obj.isCate = Number(objCan.isCate)
obj.title = objCan.title
obj.content = objCan.content
// 栈堆指向问题,指向的都是同一个地址,直接使用this.menuList即可
localStorage.setItem('Menu', JSON.stringify(this.MenuList))
this.Read()
}
// 删除方法
Del(objCan: any): void {
let i:number = this.MenuList.indexOf(objCan)
this.MenuList.splice(i,1)
localStorage.setItem('Menu', JSON.stringify(this.MenuList))
this.Read()
}
}
export default CurdMethods
第三步
App.vue文件
<template>
<div id="app">
<ul>
<li @click="cate()">全部</li>
<li @click="cate(0)">学习</li>
<li @click="cate(1)">工作</li>
<li @click="cate(2)">热门</li>
</ul>
<button @click="dialogVisible = true">新增</button>
<div class="blog">
<NavItem v-for="item in $store.state.menuList.MenuList" :key="item.id" :menuitem="item" />
</div>
<el-dialog title="提示" :visible="dialogVisible" width="30%">
标题
<el-input v-model="form.title"></el-input>
内容
<el-input v-model="form.content"></el-input>
类型
<el-select v-model="form.isCate" placeholder="选择类型">
<el-option label="学习" :value="0"></el-option>
<el-option label="工作 " :value="1"></el-option>
<el-option label="热门 " :value="2"></el-option>
</el-select>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="subMethods">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script lang="ts"> //必须要加上lang = ts
import { Component, Vue, Watch } from "vue-property-decorator";
// 引入的NavItem.vue组件 我们可以去使用在下方@Component进行挂载
import NavItem from "./components/NavItem.vue";
// 定义了一个接口 下方指定类型时使用
interface Iform {
id: number;
isCate: number;
title: string;
content: string;
}
// ts用来引组件的
@Component({
components: {
NavItem
}
})
export default class App extends Vue {
// 如果想要声明原来的data数据直接写在里面即可
// 像created mounted 也是可以直接去使用的,我们不需要在用对象的方式去写代码
// 弹出框展示
dialogVisible: boolean = false;
// 增加内容展示
form: Iform = {
isCate: 0,
id: -1,
title: "",
content: ""
};
// 点击确定事件
subMethods(): void {
this.$store.commit('Add',this.form);
this.form.title = ''
this.form.content = ''
this.dialogVisible = false;
}
// 筛选类型
cate(isCate:number):void{
this.$store.commit('Cate',isCate);
}
}
</script>
<style>
ul {
display: flex;
justify-content: space-around;
}
li {
list-style-type: none;
padding: 10px 30px;
font-size: 20px;
font-weight: 800;
background: chartreuse;
}
.blog {
display: flex;
flex-wrap: wrap;
}
</style>
第四步
src/component/NavItem.vue
<template>
<div class="content">
<div class="edit" @click="editMethods">Edit</div>
<p>标题:{{ menuitem.title }}</p>
<p>内容:{{ menuitem.content }}</p>
<p>所属分类:{{ cate }}</p>
<div class="del" @click="delMethods">Del</div>
<el-dialog title="修改" :visible="dialogVisible" width="30%">
标题
<el-input v-model="form.title"></el-input>
内容
<el-input v-model="form.content"></el-input>
类型
<el-select v-model="form.isCate" placeholder="选择类型">
<el-option label="学习" :value="0"></el-option>
<el-option label="工作 " :value="1"></el-option>
<el-option label="热门 " :value="2"></el-option>
</el-select>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="subMethods">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Watch } from "vue-property-decorator";
interface Iform {
id: number;
isCate: number;
title: string;
content: string;
}
@Component
export default class NavItem extends Vue {
cate!: string; // 判断类型
// 数据初始化双绑
form: Iform = {
id: -1,
isCate: 0,
title: "",
content: ""
};
dialogVisible: boolean = false;
// 获取 父组件传值
@Prop() menuitem!: any;
// watch侦听器
@Watch("menuitem", { /*immediate: true,*/ deep: true }) //用于深度监听对象 immediate上来就执行
getmenuitem(newVal: object, oldVal: object) {
if (this.menuitem.isCate === 0) {
this.cate = "学习";
} else if (this.menuitem.isCate === 1) {
this.cate = "工作";
} else {
this.cate = "热门";
}
}
// 修改方法
editMethods() {
this.dialogVisible = true;
this.form.id = this.menuitem.id;
this.form.isCate = this.menuitem.isCate;
this.form.title = this.menuitem.title;
this.form.content = this.menuitem.content;
}
// 提交修改事件方法
subMethods() {
this.$store.commit('Edit',this.form);
this.dialogVisible = false;
}
// 删除事件
delMethods() {
this.$confirm("此操作将永久删除该文件, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
this.$store.commit('Del',this.form);
this.$message({
type: "success",
message: "删除成功!"
});
})
.catch(() => {
this.$message({
type: "info",
message: "已取消删除"
});
});
}
created() {
// 初始化判断类型
if (this.menuitem.isCate === 0) {
this.cate = "学习";
} else if (this.menuitem.isCate === 1) {
this.cate = "工作";
} else {
this.cate = "热门";
}
}
}
</script>
<style scoped>
.content {
width: 200px;
height: 200px;
margin: 10px;
background: #cccccc;
text-align: center;
}
.edit {
background: blueviolet;
width: 50px;
margin: 0 auto;
border-radius: 20px;
}
.del {
background: red;
width: 50px;
margin: 0 auto;
border-radius: 20px;
}
</style>
接下来我们可以看一下目录结构
最后因为我在项目中使用到了element ui
所以我们还要在项目中进行挂载
然后最后需要完善一下store中的方法
store/index.ts
import Vue from 'vue'
import Vuex from 'vuex'
import MenuList from '../model/CurdMethods'
const store = new Vuex.Store({
state: {
menuList: new MenuList()
},
mutations: {
// 下方的方法其实就是在调用我们new MenuList()这个实例对象中的方法
Add(state: any, obj: any) {
state.menuList.Add(obj)
},
Edit(state: any, obj: any){
state.menuList.Edit(obj)
},
Del(state: any, obj: any){
state.menuList.Del(obj)
},
// 点击类型进行分类判断的
Cate(state:any,isCate:number){
if(isCate==0 || isCate==1 || isCate==2){
state.menuList.MenuList = state.menuList.Read().filter((item:any)=>{
return isCate === item.isCate
})
}else{
state.menuList.MenuList = state.menuList.Read()
}
}
}
})
export default store
main.ts
import Vue from 'vue'
import App from './App.vue'
import store from './store'
// 引入 element ui 并挂载
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
Vue.config.productionTip = false
new Vue({
store,
render: h => h(App)
}).$mount('#app')
最终启动项目 呈现效果
码云地址:https://gitee.com/hanbingxu82/vue-ts-curd-local-strage