快乐宝爸 發表於 2020-1-16 22:17:00

在 Vue 中使用 Typescript

<h2><span style="color: rgba(216, 14, 75, 1)">前言</span></h2>
<p>  <span style="font-size: 18px">恕我直言,用 Typescript 写 Vue 真的很难受,Vue 对 ts 的支持一般,如非万不得已还是别在 Vue 里边用吧,不过听说 Vue3 会增强对 ts 的支持,正式登场之前还是期待一下吧嘻嘻。</span></p>
<p><span style="font-size: 18px">  本篇不会过多讲述 ts 语法,着重记录下 在 Vue 中使用 ts 的方法以及踩坑经过。</span></p>
<p><span style="font-size: 18px">  如果是使用 Vue Cli2 搭建的项目,要注意 webpack 版本可能与&nbsp;ts-loader 版本不匹配,可以降低 ts-loader 版本到 3.0+ 或者 将 webpack升级到 4.0+ (本篇所用版本 <span style="color: rgba(216, 14, 75, 1)">webpack@3.6.0 + ts-loader@3.5.0</span>)</span></p>
<h2><span style="color: rgba(216, 14, 75, 1)">主要步骤</span></h2>
<p><span style="font-size: 18px">  1. 先要让 vue 项目可以识别 .ts 文件。安装关键依赖 <span style="color: rgba(216, 14, 75, 1)">npm i typescript ts-loader -D</span></span></p>
<p><span style="font-size: 18px">  2. 在 webpack.base.config.js 中<span style="color: rgba(216, 14, 75, 1)">添加 ts-loader</span></span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">resolve.extensions 里面加上.ts 后缀 之后引入.ts的时候可以不写后缀</span>
<span style="color: rgba(0, 0, 0, 1)">{
test: </span>/\.tsx?$/<span style="color: rgba(0, 0, 0, 1)">,
loader: </span>'ts-loader'<span style="color: rgba(0, 0, 0, 1)">,
exclude: </span>/node_modules/<span style="color: rgba(0, 0, 0, 1)">,
options: {
    appendTsSuffixTo: [</span>/\.vue$/], <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">关键</span>
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<p>  <span style="font-size: 18px">3.&nbsp;在根目录建<span style="color: rgba(216, 14, 75, 1)">tsconfig.json</span>文件 下面的配置仅供参考</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">{
</span>"include"<span style="color: rgba(0, 0, 0, 1)">: [
    </span>"src/**/*"<span style="color: rgba(0, 0, 0, 1)">
],
</span>"exclude"<span style="color: rgba(0, 0, 0, 1)">: [
    </span>"node_modules"<span style="color: rgba(0, 0, 0, 1)">
],
</span>"compilerOptions"<span style="color: rgba(0, 0, 0, 1)">: {
    </span>"allowSyntheticDefaultImports": <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
    </span>"experimentalDecorators": <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
    </span>"allowJs": <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
    </span>"module": "esnext"<span style="color: rgba(0, 0, 0, 1)">,
    </span>"target": "es5"<span style="color: rgba(0, 0, 0, 1)">,
    </span>"moduleResolution": "node"<span style="color: rgba(0, 0, 0, 1)">,
    </span>"isolatedModules": <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
    </span>"lib"<span style="color: rgba(0, 0, 0, 1)">: [
      </span>"dom"<span style="color: rgba(0, 0, 0, 1)">,
      </span>"es5"<span style="color: rgba(0, 0, 0, 1)">,
      </span>"es2015.promise"<span style="color: rgba(0, 0, 0, 1)">
    ],
    </span>"sourceMap": <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
    </span>"pretty": <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
}
}</span></pre>
</div>
<p>  <span style="font-size: 18px">4. 接着需要确保在 vue 中可以正常使用 ts 。安装&nbsp;vue-class-component(为vue组件提供修饰器) <span style="color: rgba(216, 14, 75, 1)">vue-property-decorator</span>(更多的结合vue特性的修饰器) [ tslint tslint-loader tslint-config-standard(可选 约束.ts/.tsx代码格式)],这样就可以在 vue 文件中使用诸如 @Component、@Prop等装饰器了。注意:.vue 文件中的 script 标签要加上 <span style="color: rgba(216, 14, 75, 1)">lang="ts"</span>。关于 装饰器的使用可以参看下这位大哥的文章:https://segmentfault.com/a/1190000019906321</span></p>
<div class="cnblogs_code">
<pre>&lt;template&gt;
    &lt;div class="count-down" v-html="countDown(endDate)"&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;script lang="ts"&gt;<span style="color: rgba(0, 0, 0, 1)">
import { Component, Prop, Vue, Emit } from </span>"vue-property-decorator"<span style="color: rgba(0, 0, 0, 1)">
import moment from </span>'moment'<span style="color: rgba(0, 0, 0, 1)">

@Component
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class CountDown extends Vue {
    @Prop() endDate</span>!<span style="color: rgba(0, 0, 0, 1)">: string
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 变量后加!是非空断言</span>
<span style="color: rgba(0, 0, 0, 1)">
    now: any </span>=<span style="color: rgba(0, 0, 0, 1)"> moment()
    mounted() {
      setInterval((): </span><span style="color: rgba(0, 0, 255, 1)">void</span> =&gt;<span style="color: rgba(0, 0, 0, 1)">{
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.now =<span style="color: rgba(0, 0, 0, 1)"> moment()
      },</span>1000<span style="color: rgba(0, 0, 0, 1)">)
    }
    destroyed() {
      
    }
    get countDown(): object{
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(endDate: any): string {
            let m1: any </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.now
            let m2: any </span>=<span style="color: rgba(0, 0, 0, 1)"> moment(endDate)
            let du: any </span>= moment.duration(m2 - m1, 'ms'<span style="color: rgba(0, 0, 0, 1)">)
            let hours: number </span>= du.get('hours'<span style="color: rgba(0, 0, 0, 1)">)
            let mins: number </span>= du.get('minutes'<span style="color: rgba(0, 0, 0, 1)">)
            let ss: number </span>= du.get('seconds'<span style="color: rgba(0, 0, 0, 1)">)
            </span><span style="color: rgba(0, 0, 255, 1)">if</span>(hours &lt;= 0 &amp;&amp; mins &lt;= 0 &amp;&amp; ss &lt;= 0<span style="color: rgba(0, 0, 0, 1)">) {
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> this.$emit('timeout')</span>
                <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.timeout()
                </span><span style="color: rgba(0, 0, 255, 1)">return</span> "今日已结束"<span style="color: rgba(0, 0, 0, 1)">
            }</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
                </span><span style="color: rgba(0, 0, 255, 1)">return</span> `${<span style="color: rgba(0, 0, 255, 1)">this</span>.PrefixInteger(hours,2)} &lt;span style="font-size: 16px;"&gt;小时&lt;/span&gt; ${this.PrefixInteger(mins,2)} &lt;span style="font-size: 16px;"&gt;分钟&lt;/span&gt;&lt;span style="color: #F56C6C;"&gt; ${<span style="color: rgba(0, 0, 255, 1)">this</span>.PrefixInteger(ss,2)} &lt;/span&gt;&lt;span style="font-size: 16px;"&gt;秒&lt;/span&gt;<span style="color: rgba(0, 0, 0, 1)">`
            }
      }
    }

    @Emit()
    timeout(){}

   
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">数字前补 0 </span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> num传入的数字,n需要的字符长度</span>
<span style="color: rgba(0, 0, 0, 1)">    PrefixInteger(num: number, n: number): string {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> (Array(n).join('0') + num).slice(-<span style="color: rgba(0, 0, 0, 1)">n)
    }
}
</span>&lt;/script&gt;

&lt;style lang="less" scoped&gt;
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">...</span>
&lt;/style&gt;</pre>
</div>
<p>&nbsp;</p>
<p>  <span style="font-size: 18px">5.&nbsp;将main.js 变成 main.ts 并且在 webpack.base.conf.js 修改入口为<span style="color: rgba(216, 14, 75, 1)">main.ts</span>,这一步至关重要。</span></p>
<p><span style="font-size: 18px">  6. 在 src 目录下新建文件 <span style="color: rgba(216, 14, 75, 1)">shims-vue.d.ts</span> ,告诉 TypeScript *.vue 后缀的文件可以交给 vue 模块来处理,注意&nbsp;在代码中导入 *.vue 文件的时候,需要写上 .vue 后缀,这里可以参考官网说明:增强类型以配合插件使用</span><br></p>
<div class="cnblogs_code">
<pre>declare module "*.vue"<span style="color: rgba(0, 0, 0, 1)"> {
    import Vue from </span>"vue"<span style="color: rgba(0, 0, 0, 1)">;
    export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> Vue;
}</span></pre>
</div>
<h2><span style="color: rgba(216, 14, 75, 1)">踩坑记录</span></h2>
<p>  <span style="font-size: 18px">1. 报错如下,报错原因一是没有将入口文件改成 ts,二是&nbsp;webpack.base.conf.js 中引入 ts-loader 错误,没有加上 options: { appendTsSuffixTo: [/\.vue$/], }</span></p>
<div class="cnblogs_code">
<pre>Module build failed: Error: <span style="color: rgba(216, 14, 75, 1)">Could not find source file: 'XXX/src/App.vue'.</span></pre>
</div>
<p>  <span style="font-size: 18px">2. <span style="color: rgba(216, 14, 75, 1)">全局属性报错 如 Vue.prototype.$msg = XXX</span>,需要将全局属性在&nbsp;.d.ts 文件中声明</span></p>
<div class="cnblogs_code">
<pre>import Vue from "vue"<span style="color: rgba(0, 0, 0, 1)">;
import { AxiosInstance } from </span>"axios"<span style="color: rgba(0, 0, 0, 1)">;
import { ElMessage } from </span>"element-ui/types/message"<span style="color: rgba(0, 0, 0, 1)">;

declare module </span>"*.vue"<span style="color: rgba(0, 0, 0, 1)"> {
    export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> Vue;
}

declare module </span>'vue/types/vue'<span style="color: rgba(0, 0, 0, 1)"> {
    interface Vue {
      $http: AxiosInstance,
      $message: ElMessage
    }
}</span></pre>
</div>
<p>  <span style="font-size: 18px">3. 使用上面这种声明全局属性的方式又会带来新的问题,报错<span style="color: rgba(216, 14, 75, 1)">&nbsp;import App from './App.vue'处,找不到 App.vue 这个模块</span>,虽然不影响编译,但是这红色的波浪线就像老鼠屎,看着那叫一个难受呀。解决方法:将 shims-vue.d.ts 文件一分为二,将全局属性声明和 Vue 的声明分离;在 shims-vue.d.ts 文件同级目录下新建 vue.d.ts(名字不一定叫 vue,如 xxx.d.ts 也可以);关键是要将以下代码放在单独的 .d.ts 文件中</span></p>
<div class="cnblogs_code">
<pre>declare module '*.vue'<span style="color: rgba(0, 0, 0, 1)"> {
import Vue from </span>'vue'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> Vue
}</span></pre>
</div><br><br>
来源:https://www.cnblogs.com/eightFlying/p/vue_typescript.html
頁: [1]
查看完整版本: 在 Vue 中使用 Typescript