教你搭建typescript的vue项目

自尤大神去年9月推出vue对typescript的支持后,一直想开箱尝试vue+ts,最近一个新项目准备入手typescript,也遇到了很多坑,下面就一步步来吧!!!

1. 项目创建和初始化

1.1 安装脚手架、创建项目

全局安装 vue-cli脚手架

$ npm install -g @vue/cli
复制代码

等待安装完成后开始下一步,检查是否安装成功: Vue -v

1.2. 初始化项目

$ vue create vue-ts
复制代码
  1. 选择预设的模板 选择更多功能 Manully select features
    回车后来到选择插件
  2. 选择插件
    这边选择了(Babel、Typescript、Router、Css预处理器、Linter / Formatter 格式检查、Unit测试框架)
    使用键盘空格选择插件
  3. 自动检测typescript(yes)
  4. 路由模式选择
    是否使用 history模式的路由 (Yes)
  5. 选择一个css预处理器 (Sass/SCSS)
  6. 选择格式检查规则(什么时候进行 tslint 校验: Line on save)
  7. 是否保存这份预设配置?(yes)
    选是的话,下次创建一个vue项目,可以直接使用这个预设文件,而无需再进行新的配置,直接使用选择的模板创建项目

等待所有的依赖完成

通过node生产组件和页面是基于lentoo的vuecli3-project基础上稍作更改

2. 通过node来生成组件

安装chalk

$ npm install chalk --save-dev
复制代码

在根目录中创建一个 scripts 文件夹,

2.1. 通过node来生成组件

新增一个generateComponent.js文件,放置生成组件的代码、

新增一个template.js文件,放置组件模板的代码 template.js文件

    /*** 将驼峰命名转为中横杠例如:PlInputGroup --> pl-input-group* @param str */
function toMiddleLine (str) {let temp = str.replace(/[A-Z]/g,function (match) {return "-" + match.toLowerCase()});if (temp.slice(0, 1) === '-') { //如果首字母是大写,执行replace时会多一个-,这里需要去掉temp = temp.slice(1)}return temp;
}/*** 首字母大写* @param {*} str 字符串* @returns*/
function initialToUp (str) {  return str.slice(0, 1).toUpperCase() + str.slice(1);
}module.exports = {
vueTemplate: componentName => {return `<template><div class="${toMiddleLine(componentName)}">${toMiddleLine(componentName)}</div>
</template><script lang="ts">import { Vue, Component, Prop, Watch, Emit, Provide, Inject } from 'vue-property-decorator'@Component({})export default class ${initialToUp(componentName)} extends Vue {}
</script><style lang="scss" scoped>
// @import './style.scss';
.${toMiddleLine(componentName)} {}
</style>`
},
styleTemplate: componentName => {return `.${toMiddleLine(componentName)} {}`
},
entryTemplate: `import Main from './main.vue'
export default Main
`
}复制代码

generateComponent.js文件

const chalk = require('chalk')
const path = require('path')
const fs = require('fs')const resolve = (...file) => path.resolve(__dirname, ...file)
const log = message => console.log(chalk.green(`${message}`))
const successLog = message => console.log(chalk.blue(`${message}`))
const errorLog = error => console.log(chalk.red(`${error}`))
const { vueTemplate, entryTemplate, styleTemplate } = require('./template')const generateFile = (path, data) => {if (fs.existsSync(path)) {errorLog(`${path}文件已存在`)return}return new Promise((resolve, reject) => {fs.writeFile(path, data, 'utf8', err => {if (err) {errorLog(err.message)reject(err)} else {resolve(true)}})})
}
log('请输入要生成的组件名称、如需生成全局组件,请加 global/ 前缀')
let componentName = ''
process.stdin.on('data', async chunk => {const inputName = String(chunk).trim().toString()/*** 组件目录路径*/const componentDirectory = resolve('../src/components', inputName)/*** vue组件路径*/const componentVueName = resolve(componentDirectory, 'main.vue')/*** 入口文件路径*/const entryComponentName = resolve(componentDirectory, 'index.ts')/*** style样式路径*/const styleComponentName = resolve(componentDirectory, 'style.less')const hasComponentDirectory = fs.existsSync(componentDirectory)if (hasComponentDirectory) {errorLog(`${inputName}组件目录已存在,请重新输入`)return} else {log(`正在生成 component 目录 ${componentDirectory}`)await dotExistDirectoryCreate(componentDirectory)// fs.mkdirSync(componentDirectory);}try {if (inputName.includes('/')) {const inputArr = inputName.split('/')componentName = inputArr[inputArr.length - 1]} else {componentName = inputName}log(`正在生成 vue 文件 ${componentVueName}`)await generateFile(componentVueName, vueTemplate(componentName))log(`正在生成 entry 文件 ${entryComponentName}`)await generateFile(entryComponentName, entryTemplate)log(`正在生成 style 文件 ${styleComponentName}`)await generateFile(styleComponentName, styleTemplate(componentName))successLog('生成成功')} catch (e) {errorLog(e.message)}process.stdin.emit('end')
})
process.stdin.on('end', () => {log('exit')process.exit()
})
function dotExistDirectoryCreate (directory) {return new Promise((resolve) => {mkdirs(directory, function () {resolve(true)})})
}// 递归创建目录
function mkdirs (directory, callback) {var exists = fs.existsSync(directory)if (exists) {callback()} else {mkdirs(path.dirname(directory), function () {fs.mkdirSync(directory)callback()})}
}复制代码

配置package.json

"new:comp": "node ./scripts/generateComponent"
复制代码

执行 npm / cnpm / yarn run new:comp 生成组件

2.2. 通过node来生成页面组件

在scripts目录下新建一个generateView.js文件

generateView.js文件

const chalk = require('chalk')
const path = require('path')
const fs = require('fs')const resolve = (...file) => path.resolve(__dirname, ...file)
const log = message => console.log(chalk.green(`${message}`))
const successLog = message => console.log(chalk.blue(`${message}`))
const errorLog = error => console.log(chalk.red(`${error}`))
const { vueTemplate } = require('./template')const generateFile = (path, data) => {if (fs.existsSync(path)) {errorLog(`${path}文件已存在`)return}return new Promise((resolve, reject) => {fs.writeFile(path, data, 'utf8', err => {if (err) {errorLog(err.message)reject(err)} else {resolve(true)}})})
}
log('请输入要生成的页面组件名称、会生成在 views/目录下')
let componentName = ''
process.stdin.on('data', async chunk => {const inputName = String(chunk).trim().toString()/*** Vue页面组件路径*/let componentVueName = resolve('../src/views', inputName)// 如果不是以 .vue 结尾的话,自动加上if (!componentVueName.endsWith('.vue')) {componentVueName += '.vue'}/*** vue组件目录路径*/const componentDirectory = path.dirname(componentVueName)const hasComponentExists = fs.existsSync(componentVueName)if (hasComponentExists) {errorLog(`${inputName}页面组件已存在,请重新输入`)return} else {log(`正在生成 component 目录 ${componentDirectory}`)await dotExistDirectoryCreate(componentDirectory)}try {if (inputName.includes('/')) {const inputArr = inputName.split('/')componentName = inputArr[inputArr.length - 1]} else {componentName = inputName}log(`正在生成 vue 文件 ${componentVueName}`)await generateFile(componentVueName, vueTemplate(componentName))successLog('生成成功')} catch (e) {errorLog(e.message)}process.stdin.emit('end')
})
process.stdin.on('end', () => {log('exit')process.exit()
})
function dotExistDirectoryCreate (directory) {return new Promise((resolve) => {mkdirs(directory, function () {resolve(true)})})
}// 递归创建目录
function mkdirs (directory, callback) {var exists = fs.existsSync(directory)if (exists) {callback()} else {mkdirs(path.dirname(directory), function () {fs.mkdirSync(directory)callback()})}
}复制代码

配置package.json

"new:view": "node ./scripts/generateView"
复制代码

执行 npm / cnpm / yarn run new:view 生成页面

3. vue与typescript结合

3.1. 首先组件声明

若对vue-property-decorator库不了解的,请点击vue-property-decorator的了解更多

创建组件如下:

<script lang="ts">import { Component, Prop, Vue, Watch, Emit, Provide, Inject } from 'vue-property-decorator'@Componentexport default class Test extends Vue {}
</script>
复制代码

3.2. data定义

若对ts的基本类型不了解的, 请点击 typescript中文文档

  private listTotal: number = 50private form: any = {addText: [],addTextarea: [],text: '',textarea: '',imgUrl: ''}
复制代码

3.3 props声明

  // align justify 弹性布局对齐方式@Prop({default: 'center'})private align!: string@Prop({default: 'flex-start'})private justify!: string// 千万不要这样定义 @Prop private align: string = 'center' ---> 踩
</script>
复制代码

3.4 vue生命周期及自定义方法

methods不需要像vue里面 methods: { text () {return console.log(222)} }

  public created (): void {}public mounted (): void {}public handleClick () {} // methods定义
复制代码

3.5 Watch

  // 监听路由变化@Watch('$route')onRouteChanged(route: any, oldRoute: any):void {console.log(route, oldRoute)}
复制代码

3.6 computed

 public get msg () {return 'from typescript'}
复制代码

3.7 Emit

  @Emit('change')private methodName(x: number, y: string) {console.log('child to parent a value')}
复制代码

5. 踩坑

5.1 tinymac富文本编辑器的结合ts的使用,tiny中文文档

引入tinymac的时候,会报错

解决方法:src目录下面新建一个shims-tinymce.d.ts文件

declare module 'tinymce/tinymce'
复制代码

重新启动项目就ok了

5.2 主题、样式、语言配置

  1. 主题

引入主题报错import 'tinymce/themes/modern/theme'

可以使用sliver主题

   import 'tinymce/themes/silver/theme'
复制代码
  1. 样式及语言汉化

在public目录新建的static文件

2.1 将node_modules/tinymce/skins文件拷贝到static中

2.2 zh_CN.js 下载,拷贝到static文件中

5.3 引入主题,样式,语言包

配置如下

public editorInit: any = {language_url: '/static/zh_CN.js',language: 'zh_CN',selector: 'textarea',skin_url: '/static/skins/ui/oxide',height: 300,browser_spellcheck: true, // 拼写检查branding: false, // 去水印// elementpath: false,  //禁用编辑器底部的状态栏statusbar: false, // 隐藏编辑器底部的状态栏paste_data_images: true, // 允许粘贴图像plugins: setPlugins,toolbar: setToolbar,// 启用菜单栏并显示如下项 [文件 编辑 插入 格式 表格]menubar: 'file edit insert view format table',// 配置每个菜单栏的子菜单项(如下是默认配置)menu: {file: {title: 'File',items: 'newdocument'},edit: {title: 'Edit',items: 'undo redo | cut copy paste pastetext | selectall'},insert: {title: 'Insert',items: 'link media | template hr'},view: {title: 'View',items: 'visualaid'},format: {title: 'Format',items: 'bold italic underline strikethrough superscript subscript | formats | removeformat'},table: {title: 'Table',items: 'inserttable tableprops deletetable | cell row column'}},// 覆盖默认的字体单位为ptfontsize_formats: '8px 10px 12px 14px 16px 18px 20px 24px 36px',/*** 下面方法是为tinymce添加自定义插入图片按钮* 也可以借助elementui的Upload组件,上传图片*/images_upload_url: '/api/image', // 上传图片接口地址images_upload_handler: (blobInfo: any, success: any, failure: any) => {let xhr: any = nulllet formData: any = nullxhr = new XMLHttpRequest()xhr.withCredentials = falsexhr.open('POST', this.$store.state.imgUrl)xhr.onload = () => {if (xhr.status < 200 || xhr.status >= 300) {failure(xhr.status)return}let json = JSON.parse(xhr.responseText)if (json.code === 0) {success(json.data[0].newFileName)} else {failure('HTTP Error: ' + json.msg)}}formData = new FormData()formData.append('file', blobInfo.blob(), blobInfo.filename())xhr.send(formData)}}
复制代码

附上效果图:

小结

前端小菜鸡,各位大神有发现不足之处请告知,谢谢!!, 项目地址后期更新

相关资源链接

  1. TypeScript 体系调研报告
  2. ts通俗易懂文档
  3. ts中文文档
  4. vue-cli3 项目从搭建优化到docker部署

vue-typescript相关推荐

  1. Vue + TypeScript + Element 搭建简洁时尚的博客网站及踩坑记

    前言 本文讲解如何在 Vue 项目中使用 TypeScript 来搭建并开发项目,并在此过程中踩过的坑 . TypeScript 具有类型系统,且是 JavaScript 的超集,TypeScript ...

  2. vue-property-decorator vue typescript写法

    什么是Typescript TypeScript 是一种由微软开发的自由和开源的编程语言,它是 JavaScript 的一个超集,扩展了 JavaScript 的语法.作者是安德斯大爷, Delphi ...

  3. vue + typescript 父子组件传值记录

    为什么80%的码农都做不了架构师?>>>    typescript 使用 vue-property-decorator 插件后 组件通信 做一个记录 1. 父传子 父: <s ...

  4. vue+typescript(vben-admin)前端开发

    小小笔记,持续总结吧. 近期遇到一个问题,总是不长记性,在此记录一下. 我觉得大家应该会遇到跟我一样的坑,就是vue3的写法跟vue2已经不太一样了. typescript的地方如果用vue3的写法, ...

  5. 【百度地图】制作多途经点的线路导航,模拟运动 (vue,typescript)

    网上有从起点到终点的模拟线路和运动,途经点的比较少. 结合网上demo,再进行了一下修改. VUE (Typescript),先在index.html里引用 <!DOCTYPE html> ...

  6. Vue+TypeScript使用教程-快速入门

    目前Vue CLI已经内置了TypeScript工具支持 一.使用 TypeScript 创建工程 vue create vue-ts 选择Manually Select Features 然后选择自 ...

  7. vue+typescript项目中无法使用vue-print-nb 打印

    最优雅的方法 感谢评论区大佬netzhouxiang_提供的方法 declare module 'vue-print-nb'; 问题 vue项目改造成对typescript支持的项目后,vue-pri ...

  8. Vue—Typescript

    Vue-cli推出了3.0,自带了Pwa和Typescript,尝试了下发现Typescript用起来十分方便,这里简单介绍下. 这次脚手架项目中用到了2个插件,vue-class-component ...

  9. Vue Typescript @Prop

    语法:@Prop(options: (PropOptions | Constructor[] | Constructor) = {})参数说明:@Prop装饰器接收一个参数,这个参数可以有三种写法:C ...

  10. vue+typescript怎么写computed

    export default class xx extends Vue {private get greet(): string {return 'hey' + this.name} } 参考:htt ...

最新文章

  1. matlab参数数目不足lorenz,求指导,MATLAB程序,老是提示这个“未定义与 'double' 类型的输入参数相对应的函数 'genfisl'。”错误,...
  2. 【读书笔记】《第一行代码》
  3. java使用集合模拟斗地主洗牌发牌案例
  4. 软件项目管理0718:读一页项目管理读书笔记
  5. java 偏移符号_java中的移位运算符总结
  6. python运算符中用来计算整商的是什么_零基础学python,看完这篇文章,你的python基础就差不多了...
  7. 了解Web及网络基础——《图解http》第一章
  8. 为什么人类的大脑认为数学“美”?
  9. opencv运行出现错误
  10. 小程序和网站中无限滚动的实现
  11. C++ 读取txt文件方法读取速度比较
  12. 线性代数 向量 知识技巧思维导图 [21考研上岸之旅]
  13. quartus基本操作
  14. Matlab求特征值和特征向量 + find函数的基本简单用法
  15. python 识别汉字、数字、字母,实现半角及全角之间的转换
  16. 自然语言处理 第二期
  17. 武安高中计算机学院那个好,河北邯郸最好的5所高中,邯郸一中排第一无争议,你还知道几所?...
  18. cacti 画图 断续问题处理
  19. bash输出颜色字体
  20. 【树莓派】树莓派SD卡测速一键脚本,SD卡读写速度测试程序

热门文章

  1. 面试官问:跨域请求如何携带cookie?
  2. 面试官问发布订阅模式是在问什么?
  3. 学习 lodash 源码整体架构,打造属于自己的函数式编程类库
  4. Django——Model
  5. 以太坊钱包开发系列 - 创建钱包账号
  6. flex 布局以及样式
  7. jQuery 基金会和 Dojo 基金会合并:Open Web
  8. WIX、Squarespace、WordPress 三者的优劣分别是什么?
  9. Django学习笔记(4)
  10. 奇迹暖暖服务器不稳定,闪耀暖暖用土豆当服务器?开服仅半小时就崩溃,无数玩家疯狂吐槽...