什么是typescript
TypeScript是JavaScript 的强类型版本。然后在编译期去掉类型和特有语法,生成纯粹的JavaScript 代码。由于最终在浏览器中运行的仍然是JavaScript ,所以TypeScript并不依赖于浏览器的支持,也并不会带来兼容性问题。
TypeScript是JavaScript的超集,这意味着他支持所有的JavaScript语法。并在此之上对JavaScript添加了一些扩展,如 class / interface / module 等。这样会大大提升代码的可阅读性。
与此同时,TypeScript也是JavaScript ES6 的超集,Google的 Angular 2.0也宣布采用 TypeScript 进行开发。这更是充分说明了这是一门面向未来并且脚踏实地的语言。
强类型语言的优势在于静态类型检查,概括来说主要包括以下几点:
- 静态类型检查
- IDE 智能提示
- 代码重构
- 可读性
静态类型检查可以避免很多不必要的错误, 不用在调试的时候才发现问题
前言
Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统。有三个组件:
- CLI:@vue/cli 全局安装的 npm 包,提供了终端里的vue命令(如:vue create 、vue serve 、vue ui 等命令)
- CLI 服务:@vue/cli-service是一个开发环境依赖。构建于 webpack 和 webpack-dev-server 之上(提供 如:serve、build 和 inspect 命令)
- CLI 插件:给Vue 项目提供可选功能的 npm 包 (如: Babel/TypeScript 转译、ESLint 集成、unit和 e2e测试 等)
初始化项目
1.安装vue-cli3.0
如果你之前安装的是vue-cli2.0版本的,那么你需要先卸载2.0版本,再安装3.0版本。
// cnpm 为淘宝镜像
cnpm uninstall vue-cli -g // 卸载
cnpm install -g @vue/cli // 安装新版本
2.创建项目
vue create my-vue-cli3-ts
选择Manually select features回车
按照下图所示选中(空格选中)回车安装插件
然后一路回车,放一下配置图
安装完以后,进入项目并启动项目
cd my-vue-cli3-ts
npm run serve
启动后显示如下:
代码的工作目录如下:
3.配置文件并改变文件结构
- 打开根目录下的vue.config.js,如果没有就自己创建一个,放在根目录下,并添加以下内容
const path = require('path')
const resolve = dir => {
return path.join(__dirname, dir)
}
// 线上打包路径,请根据项目实际线上情况
const BASE_URL = process.env.NODE_ENV === 'production' ? '/' : '/'
module.exports = {
publicPath: BASE_URL,
outputDir: 'dist', // 打包生成的生产环境构建文件的目录
assetsDir: '', // 放置生成的静态资源路径,默认在outputDir
indexPath: 'index.html', // 指定生成的 index.html 输入路径,默认outputDir
pages: undefined, // 构建多页
lintOnSave: true,// 是否在保存的时候检查
productionSourceMap: false, // 开启 生产环境的 source map?
chainWebpack: config => {
// 配置路径别名
config.resolve.alias
.set('@', resolve('src'))
.set('_c', resolve('src/components'))
},
css: {
modules: false, // 启用 CSS modules
extract: true, // 是否使用css分离插件
sourceMap: false, // 开启 CSS source maps?
loaderOptions: {} // css预设器配置项
},
devServer: { // 环境配置
open: process.platform === "darwin",
host: 'localhost',
port: 8080, // 端口
https: false,
hotOnly: false,
open: true, //配置自动启动浏览器
proxy: {
"/api": {
target: '<url>',//设置你调用的接口域名和端口号 别忘了加http
changeOrigin: true,
pathRewrite: {
"/api": "" // 这里理解成用‘/api’代替target里面的地址,后面组件中我们掉接口时直接用api代替 比如我要调用'http://40.00.100.100:3002/user/add',直接写‘/api/user/add’即可
}
}
},
// string | Object
before: app => {}
},
pluginOptions: {// 第三方插件配置
// ...
}
}
- 配置一下根目录下tslint.json,编码习惯还是根据团队的要求来,这里有一份参考的tslint.json配置,如下:
{
"defaultSeverity": "warning",
"extends": [
"tslint:recommended"
],
"linterOptions": {
"exclude": [
"node_modules/**"
]
},
"rules": {
"quotemark": false, // 字符串文字需要单引号或双引号。
"indent": false, // 使用制表符或空格强制缩进。
"member-access": false, // 需要类成员的显式可见性声明。
"interface-name": false, // 接口名要求大写开头
"ordered-imports": false, // 要求将import语句按字母顺序排列并进行分组。
"object-literal-sort-keys": false, // 检查对象文字中键的排序。
"no-consecutive-blank-lines": false, // 不允许连续出现一个或多个空行。
"no-shadowed-variable": false, // 不允许隐藏变量声明。
"no-trailing-whitespace": false, // 不允许在行尾添加尾随空格。
"semicolon": false, // 是否分号结尾
"trailing-comma": false, // 是否强象添加逗号
"eofline": false, // 是否末尾另起一行
"prefer-conditional-expression": false, // for (... in ...)语句必须用if语句过滤
"curly": true, //for if do while 要有括号
"forin": false, //用for in 必须用if进行过滤
"import-blacklist": true, //允许使用import require导入具体的模块
"no-arg": true, //不允许使用 argument.callee
"no-bitwise": true, //不允许使用按位运算符
"no-console": false, //不能使用console
"no-construct": true, //不允许使用 String/Number/Boolean的构造函数
"no-debugger": true, //不允许使用debugger
"no-duplicate-super": true, //构造函数两次用super会发出警告
"no-empty": true, //不允许空的块
"no-eval": true, //不允许使用eval
"no-floating-promises": false, //必须正确处理promise的返回函数
"no-for-in-array": false, //不允许使用for in 遍历数组
"no-implicit-dependencies": false, //不允许在项目的package.json中导入未列为依赖项的模块
"no-inferred-empty-object-type": false, //不允许在函数和构造函数中使用{}的类型推断
"no-invalid-template-strings": true, //警告在非模板字符中使用${
"no-invalid-this": true, //不允许在非class中使用 this关键字
"no-misused-new": true, //禁止定义构造函数或new class
"no-null-keyword": false, //不允许使用null关键字
"no-object-literal-type-assertion": false, //禁止object出现在类型断言表达式中
"no-return-await": true, //不允许return await
"arrow-parens": false, //箭头函数定义的参数需要括号
"adjacent-overload-signatures": false, // Enforces function overloads to be consecutive.
"ban-comma-operator": true, //禁止逗号运算符。
"no-any": false, //不需使用any类型
"no-empty-interface": true, //禁止空接口 {}
"no-internal-module": true, //不允许内部模块
"no-magic-numbers": false, //不允许在变量赋值之外使用常量数值。当没有指定允许值列表时,默认允许-1,0和1
"no-namespace": [true, "allpw-declarations"], //不允许使用内部modules和命名空间
"no-non-null-assertion": true, //不允许使用!后缀操作符的非空断言。
"no-parameter-reassignment": true, //不允许重新分配参数
"no-reference": true, // 禁止使用/// <reference path=> 导入 ,使用import代替
"no-unnecessary-type-assertion": false, //如果类型断言没有改变表达式的类型就发出警告
"no-var-requires": false, //不允许使用var module = require("module"),用 import foo = require('foo')导入
"prefer-for-of": true, //建议使用for(..of)
"promise-function-async": false, //要求异步函数返回promise
"max-classes-per-file": [true, 2], // 一个脚本最多几个申明类
"variable-name": false,
"prefer-const": false // 提示可以用const的地方
}
}
顺便贴一份自己的配置
{
"defaultSeverity": "warning",
"extends": [
"tslint:recommended"
],
"linterOptions": {
"exclude": [
"node_modules/**"
]
},
"rules": {
"quotemark": false,
"semicolon": false,
"indent": [true, "spaces", 4],
"interface-name": false,
"ordered-imports": false,
"object-literal-sort-keys": false,
"no-consecutive-blank-lines": false,
"no-var-keyword": true,
"no-var-requires": false,
"triple-equals": true,
"prefer-const": true,
"no-sparse-arrays": true,
"no-duplicate-imports": true,
"no-mergeable-namespace": true,
"encoding": true,
"trailing-comma": false,
"no-console": false,
"no-debugger": false,
"no-empty": false,
"no-string-literal": false,
"no-trailing-whitespace": false,
"comment-format": [
false,
"check-space"
],
"max-line-length": [
false,
120
]
}
}
- 改变文件结构(按照个人习惯来改变的)
1.在./src目录下创建store文件夹,并将原来./src目录下的store.ts挪到该文件夹下,同时修改./src目录下的main.ts中的store路径为import store from './store/store'
2.在./src目录下创建axios文件夹,并添加api.ts、http.ts
3.在./src目录下创建util文件夹,并添加utils.ts用于存放公共的函数
4.编写demo
以View UI(即Iview)为例
- 安装ViewUI,可以参考ViewUI安装
npm install view-design --save
- 在main.ts中引入ViewUI,如下:
import ViewUI from 'view-design';
import 'view-design/dist/styles/iview.css';
Vue.use(ViewUI);
- 在./src目录下的views文件夹下,新建一个Demo.vue文件,在components文件夹里面新建一个HelloDemo.vue文件,两个文件里面的代码如下:
// Demo.vue
<template>
<div class="demo">
<HelloDemo></HelloDemo>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloDemo from '@/components/HelloDemo.vue';
@Component({
components: {
HelloDemo,
},
})
export default class Demo extends Vue {}
</script>
// HelloDemo.vue
<template>
<div class="hello-demo">
<Button>Default</Button>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
@Component({
components: {
},
})
export default class HelloDemo extends Vue {}
</script>
- 为Demo.vue添加路由,在router.ts里面添加demo路由,可以不删除已有的代码,在后面添加
// router.ts
import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
Vue.use(Router);
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ './views/About.vue'),
},
{
path: '/demo',
name: 'demo',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ './views/Demo.vue'),
},
],
});
- 最后在App.vue文件里添加一个路由跳转,实现点击Demo时,可以跳转到Demo页面
// App.vue
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> |
<router-link to="/demo">Demo</router-link>
</div>
<router-view/>
</div>
</template>
<style lang="less">
#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>
- 启动程序
npm run serve
- 效果如下,当点击Demo的时候,跳转到demo页面,里面的按钮是ViewUI里面的样式效果
5.使用vuex
vuex在ts文件中的使用主要是利用vuex-class,vuex-class可以包装vuex的写法,使代码简化
- 安装vuex-class
关于vuex-class介绍,可以参考vuex-class
npm install vuex-class -save
- 在./src文件夹下面的store文件夹里面新建modules文件夹,在modules文件夹里面新建demo.ts,在demo.ts里面定义变量,同时也修改一下store.ts(以模块化的形式引进来),代码如下
// demo.ts
// 公用的内容
import { Commit, Dispatch } from "vuex";
export interface State {
name: string,
}
const state: State = {
name: "jf",
};
const getters = {};
const mutations = {};
const actions = {
changeName(context: { state: State }) {
state.name = "joanna"
}
};
export default {
namespaced: true,
state,
mutations,
actions,
getters
};
// store.ts
import Vue from 'vue';
import Vuex from 'vuex';
import demo from '@/store/modules/demo';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
demo
},
});
- 在HelloDemo.vue里面调用demo.ts里面的变量和函数
// HelloDemo.vue
<template>
<div class="hello-demo">
<Button @click="change">change name</Button>
<div>name: {{this.demo.name}}</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { State, Action } from "vuex-class";
@Component({
components: {
},
})
export default class HelloDemo extends Vue {
@State((state) => state.demo) private demo: any;
@Action("demo/changeName") private changeName: any; // 改变姓名
// 改变姓名
private change() {
this.changeName()
}
}
</script>
- 实现效果
界面如下,当在Demo页面,点击change name按钮时,name会由jf变成joanna
6.使用axios
- 安装axios
// 下载axios
npm i axios --S
- 在http.ts文件里面添加如下代码,我是以ViewUI界面返回来的接口来测试,有没有调通的
// http.ts
import * as Axios from "axios";
import QS from "qs";
// let baseURL: string = "https://api.github.com/repos/iview/iview";
let baseURL: string = "https://api.github.com/repos";
const axios = Axios.default.create({
baseURL: baseURL, // api请求的baseURL
timeout: 0,
withCredentials: true, // 允许跨域 cookie
headers: {
"X-Requested-With": "XMLHttpRequest",
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
},
maxContentLength: 2000,
transformResponse: [
(resdata, resheader) => {
let data: any;
try {
data = JSON.parse(resdata);
} catch (e) {
data = JSON.parse(resdata);
}
return data;
}
]
});
axios.interceptors.response.use(
(response: any) => {
return response;
},
error => {
return Promise.reject(error);
}
);
// get
export const get = (req: any) => {
return axios.get(req.url, { params: req.data });
};
// post
export const post = (req: any) => {
if (req.url.indexOf('saveGroupPersonRule') > 0) {
return axios({ method: 'post', url: `/${req.url}`, data: req.data });
} else {
return axios({ method: 'post', url: `/${req.url}`, data: QS.stringify(req.data) });
}
};
// patch
export const put = (req: any) => {
return axios({
method: "put",
url: `/${req.url}`,
data: QS.stringify(req.data)
});
};
// delete
export const _delete = (req: any) => {
return axios({
method: "delete",
url: `/${req.url}`,
data: QS.stringify(req.data)
});
};
- http.ts文件里会有如下的提示,那是因为qs模块没有声明,所以在./src文件里新建一个typings文件夹,在该文件夹里面新建types.d.ts文件,将
declare module 'qs';
写在里面(文件夹的名字可以随意取)
- 在api.ts文件里面添加接口api,如下:
// api.ts
import { get, post, _delete} from "@/axios/http";
// 获取用户信息
export const getIview = (data: any) => {
const req = {
data,
url: 'iview/iview',
};
return get(req);
};
补充说明:上面代码是以ViewUI界面返回来的接口来测试,接口地址是:https://api.github.com/repos/iview/iview,在http.ts文件里let baseURL: string = "https://api.github.com/repos";
在api.ts文件里url: 'iview/iview',
将地址拆开了
- 在demo.ts文件里引进api里面的接口,并在
action
里面添加请求接口的函数getIview
// demo.ts
// 公用的内容
import { Commit, Dispatch } from "vuex";
import { getIview } from '@/axios/api';
export interface State {
name: string,
}
const state: State = {
name: "jf",
};
const getters = {};
const mutations = {};
const actions = {
changeName(context: { state: State }) {
state.name = "joanna"
},
// 获取页面接口
async getIview(
context: { commit: Commit; dispatch: Dispatch; state: State },
payload: any
) {
await getIview(payload).then(
(res: any): void => {
// debugger
}
);
},
};
export default {
namespaced: true,
state,
mutations,
actions,
getters
};
- 在HelloDemo.vue里面调用demo.ts里的接口函数
getIview
// HelloDemo.vue
<template>
<div class="hello-demo">
<Button @click="change">change name</Button>
<div>name: {{this.demo.name}}</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { State, Action } from "vuex-class";
@Component({
components: {
},
})
export default class HelloDemo extends Vue {
@State((state) => state.demo) private demo: any;
@Action("demo/changeName") private changeName: any; // 改变姓名
@Action("demo/getIview") private getIview: any; // 获取页面
mounted () {
this.getIview()
}
// 改变姓名
private change() {
this.changeName()
}
}
</script>
- 接口演示结果
总结
本片文章对vue-cli3.0+typescript+vuex+axios+router项目搭建步骤进行了简单的阐述,里面涉及到很多知识,以后会慢慢补充。