意么金 發表於 2020-10-17 14:21:00

在Vue项目中使用Typescript

<p>3.0迟迟没有发布release版本,现阶段在vue项目中使用Typescript需要花不小的精力在工程的配置上面。主要的工作是webpack对TS,TSX的处理,以及2.x版本下面使用class的形式书写vue&nbsp;组件的一些限制和注意事项。</p>
<p>&nbsp;</p>
<h2 id="item-1">webpack&nbsp;配置</h2>
<p>配置webpack对TS,TSX的支持,以便于我们在Vue项目中使用Typescript和tsx。</p>
<pre><code class="hljs javascript"><span class="hljs-built_in">module.exports = {
    <span class="hljs-attr">entry: <span class="hljs-string">'./index.vue',
    <span class="hljs-attr">output: { <span class="hljs-attr">filename: <span class="hljs-string">'bundle.<span class="hljs-string">js</span><span class="hljs-string">' },
    <span class="hljs-attr">resolve: {
      <span class="hljs-attr">extensions: [<span class="hljs-string">'.ts', <span class="hljs-string">'.tsx', <span class="hljs-string">'.vue', <span class="hljs-string">'.vuex']
    },
    <span class="hljs-attr">module: {
      <span class="hljs-attr">rules: [
            { <span class="hljs-attr">test: <span class="hljs-regexp">/\.vue$/, <span class="hljs-attr">loader: <span class="hljs-string">'vue-loader',
            <span class="hljs-attr">options: {
                <span class="hljs-attr">loaders: {
                  <span class="hljs-attr">ts: <span class="hljs-string">'ts-loader',
                  <span class="hljs-attr">tsx: <span class="hljs-string">'babel-loader!ts-loader',
                }
            }
            },
            {
                <span class="hljs-attr">test: <span class="hljs-regexp">/\.ts$/,
                <span class="hljs-attr">loader: <span class="hljs-string">'ts-loader',
                <span class="hljs-attr">options: { <span class="hljs-attr">appendTsSuffixTo: [<span class="hljs-regexp">/TS\.vue$/] }             },
            {
                <span class="hljs-attr">test: <span class="hljs-regexp">/\.tsx$/,
                <span class="hljs-attr">loader: <span class="hljs-string">'babel-loader!ts-loader',
                <span class="hljs-attr">options: {
                  <span class="hljs-attr">appendTsxSuffixTo: [<span class="hljs-regexp">/TSX\.vue$/]
                }
            }
      ]
    }
}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>在上面的配置中,vue文件中的TS内容将会使用ts-loader处理,而TSX内容将会按照ts-loader--&gt;babel-loader的顺序处理。</p>
<p>appendTsSuffixTo/appendTsxSuffixTo配置项的意思是说,从vue文件里面分离的script的ts,tsx(取决于&lt;script lang="xxx"&gt;&lt;/script&gt;)内容将会被加上ts或者tsx的后缀,然后交由ts-loader解析。</p>
<p>我在翻看了ts-loader上关于appendTsxSuffixTo的讨论发现,ts-loader貌似对文件后缀名称有很严格的限定,必须得是ts/tsx后缀,所以得在vue-loader extract &lt;script&gt;中内容后,给其加上ts/tsx的后缀名,这样ts-loader才会去处理这部分的内容。</p>
<p><strong>ts-loader只对tsx做语法类型检查,真正的jsx--&gt;render函数应该交由babel处理。</strong></p>
<p>所以我们还需要使用plugin-transform-vue-jsx来将vue jsx转换为真正的render函数。</p>
<pre><code class="hljs php"><span class="hljs-comment">// babel.config.json
{
<span class="hljs-string">"presets": [<span class="hljs-string">"env"],
<span class="hljs-string">"plugins": [<span class="hljs-string">"transform-vue-jsx"]
}</span></span></span></span></span></code></pre>
<p>同时,配置TS对tsx的处理为preserve,让其只对tsx做type类型检查。</p>
<pre><code class="hljs php"><span class="hljs-comment">// tsconfig.json
{
&nbsp;&nbsp;<span class="hljs-string">"compilerOptions":&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string">"jsx":&nbsp;<span class="hljs-string">"preserve",
}</span></span></span></span></code></pre>
<p>&nbsp;</p>
<h2 id="item-2">使用vue cli 4.x</h2>
<p>高版本的vue cli如4.x已经集成了vue + typescript的配置。选择<strong>use Typescript</strong>&nbsp;+&nbsp;<strong>Use class-style component syntax</strong>选项创建工程。</p>
<p>创建后的工程目录如下:<br><img src="http://api.fly63.com/vue_blog/public/Uploads/20191218/5dfa3df31ae5d.jpg"></p>
<p>在src根目录下,有两个shims.xx.d.ts的类型声明文件。</p>
<pre><code class="hljs javascript"><span class="hljs-comment">//shims.vue.d.ts
declare&nbsp;<span class="hljs-built_in">module&nbsp;<span class="hljs-string">"*.vue"&nbsp;{
&nbsp;&nbsp;<span class="hljs-keyword">import&nbsp;Vue&nbsp;<span class="hljs-keyword">from&nbsp;<span class="hljs-string">"vue";
&nbsp;&nbsp;<span class="hljs-keyword">export&nbsp;<span class="hljs-keyword">default&nbsp;Vue;
}</span></span></span></span></span></span></span></span></code></pre>
<pre><code class="hljs php"><span class="hljs-comment">// shims.jsx.d.ts
import&nbsp;Vue,&nbsp;{&nbsp;VNode&nbsp;}&nbsp;from&nbsp;<span class="hljs-string">"vue";
<span class="hljs-keyword">declare&nbsp;<span class="hljs-keyword">global&nbsp;{
&nbsp;&nbsp;<span class="hljs-keyword">namespace&nbsp;<span class="hljs-title">JSX&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;<span class="hljs-title">tslint:<span class="hljs-title">disable&nbsp;<span class="hljs-title">no-<span class="hljs-title">empty-<span class="hljs-title">interface
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-title">interface&nbsp;<span class="hljs-title">Element&nbsp;<span class="hljs-title">extends&nbsp;<span class="hljs-title">VNode&nbsp;{}
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;<span class="hljs-title">tslint:<span class="hljs-title">disable&nbsp;<span class="hljs-title">no-<span class="hljs-title">empty-<span class="hljs-title">interface
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-title">interface&nbsp;<span class="hljs-title">ElementClass&nbsp;<span class="hljs-title">extends&nbsp;<span class="hljs-title">Vue&nbsp;{}
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-title">interface&nbsp;<span class="hljs-title">IntrinsicElements&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[<span class="hljs-title">elem:&nbsp;<span class="hljs-title">string]:&nbsp;<span class="hljs-title">any;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}
}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>它们是作什么用的呢?</p>
<p>shims.vue.d.ts给所有.vue文件导出的模块<strong>声明了类型为Vue</strong>,它可以帮助IDE判断.vue文件的类型。</p>
<p>shims.jsx.d.ts 为 JSX&nbsp;语法的全局命名空间,这是因为基于值的元素会简单的在它所在的作用域里按标识符查找。当在 tsconfig 内开启了 jsx 语法支持后,其会自动识别对应的 .tsx 结尾的文件,(也就是Vue 单文件组件中&lt;script lang="tsx"&gt;&lt;/script&gt;的部分)可参考官网 tsx</p>
<p>&nbsp;</p>
<h2 id="item-3">基本用法</h2>
<p>在vue 2.x中使用class的方式书写vue组件需要依靠vue-property-decorator来对vue class做转换。</p>
<pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">script&nbsp;<span class="hljs-attr">lang=<span class="hljs-string">"ts"&gt;<span class="javascript">
<span class="hljs-keyword">import&nbsp;{&nbsp;Component,&nbsp;Prop,&nbsp;Vue&nbsp;}&nbsp;<span class="hljs-keyword">from&nbsp;<span class="hljs-string">"vue-property-decorator";
<span class="hljs-keyword">export&nbsp;<span class="hljs-keyword">default&nbsp;<span class="hljs-class"><span class="hljs-keyword">class&nbsp;<span class="hljs-keyword">extends&nbsp;<span class="hljs-title">Vue&nbsp;{
&nbsp;&nbsp;@Prop({ <span class="hljs-attr">default: <span class="hljs-string">'default msg'})&nbsp;private&nbsp;msg!:&nbsp;string;
&nbsp;&nbsp;name!:&nbsp;string;
&nbsp;&nbsp;show()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-built_in">console.log(<span class="hljs-string">"this.name",&nbsp;<span class="hljs-keyword">this.name);
&nbsp;&nbsp;}
}
<span class="hljs-tag">&lt;/<span class="hljs-name">script&gt;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>导出的class是经过Vue.extend之后的VueComponent函数(理论上class就是一个Function)。</p>
<p>其最后的结果就像我们使用Vue.extend来扩展一个Vue组件一样。</p>
<pre><code class="hljs javascript"><span class="hljs-comment">//&nbsp;创建构造器
<span class="hljs-keyword">var</span>&nbsp;Profile&nbsp;=&nbsp;Vue.extend({
&nbsp;&nbsp;<span class="hljs-attr">template:&nbsp;<span class="hljs-string">'&lt;p&gt;{{firstName}}&nbsp;{{lastName}}&nbsp;aka&nbsp;{{alias}}&lt;/p&gt;',
&nbsp;&nbsp;<span class="hljs-attr">data:&nbsp;<span class="hljs-function"><span class="hljs-keyword">function&nbsp;(<span class="hljs-params">)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">return&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-attr">firstName:&nbsp;<span class="hljs-string">'Walter',
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-attr">lastName:&nbsp;<span class="hljs-string">'White',
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-attr">alias:&nbsp;<span class="hljs-string">'Heisenberg'
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}
})

<span class="hljs-keyword">export <span class="hljs-keyword">default {
    <span class="hljs-attr">components: {
      Profile
    }
}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>注意上面的Profile组件并不是和我们平时一样写的Vue组件是一个plain object配置对象,它其实是一个VueComponent函数。</p>
<p>父组件实例化子组件的时候,会对传入的vue object 进行扩展,使用Vux.extend转换为组件函数。<br>如果components中的值本身是一个函数,就会省略这一步。这一点, 从Vue 源码中可以看出。</p>
<pre><code class="hljs nginx"><span class="hljs-attribute">if&nbsp;(isObject(Ctor))&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-attribute">Ctor&nbsp;=&nbsp;baseCtor.extend(Ctor)
}</span></span></code></pre>
<p>上面的Ctor就是在components中传入的组件,对应于上面导出的Profile组件。</p>
<p>&nbsp;</p>
<h2 id="item-4">使用vuex</h2>
<p>使用vuex-class中的装饰器来对类的属性做注解。</p>
<pre><code class="hljs javascript">
<span class="hljs-keyword">import Vue <span class="hljs-keyword">from <span class="hljs-string">'vue'<span class="hljs-keyword">import Component <span class="hljs-keyword">from <span class="hljs-string">'vue-class-component'<span class="hljs-keyword">import {
State,
Getter,
Action,
Mutation,
namespace
} <span class="hljs-keyword">from <span class="hljs-string">'vuex-class'

<span class="hljs-keyword">const</span> someModule = namespace(<span class="hljs-string">'path/to/module')

@Component
<span class="hljs-keyword">export <span class="hljs-class"><span class="hljs-keyword">class <span class="hljs-title">MyComp <span class="hljs-keyword">extends <span class="hljs-title">Vue {
@State(<span class="hljs-string">'foo') stateFoo
@State(<span class="hljs-function"><span class="hljs-params">state =&gt; state.bar) stateBar
@Getter(<span class="hljs-string">'foo') getterFoo
@Action(<span class="hljs-string">'foo') actionFoo
@Mutation(<span class="hljs-string">'foo') mutationFoo
@someModule.Getter(<span class="hljs-string">'foo') moduleGetterFoo

<span class="hljs-comment">// If the argument is omitted, use the property name
<span class="hljs-comment">// for each state/getter/action/mutation type
@State foo
@Getter bar
@Action baz
@Mutation qux

created () {
    <span class="hljs-keyword">this.stateFoo <span class="hljs-comment">// -&gt; store.state.foo
    <span class="hljs-keyword">this.stateBar <span class="hljs-comment">// -&gt; store.state.bar
    <span class="hljs-keyword">this.getterFoo <span class="hljs-comment">// -&gt; store.getters.foo
    <span class="hljs-keyword">this.actionFoo({ <span class="hljs-attr">value: <span class="hljs-literal">true }) <span class="hljs-comment">// -&gt; store.dispatch('foo', { value: true })
    <span class="hljs-keyword">this.mutationFoo({ <span class="hljs-attr">value: <span class="hljs-literal">true }) <span class="hljs-comment">// -&gt; store.commit('foo', { value: true })
    <span class="hljs-keyword">this.moduleGetterFoo <span class="hljs-comment">// -&gt; store.getters['path/to/module/foo']
}
}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>&nbsp;</p>
<h2 id="item-5">mixin</h2>
<p>对于mixin,我们使用class的继承很容易实现类似功能。</p>
<pre><code class="hljs javascript"><span class="hljs-keyword">import&nbsp;Vue&nbsp;<span class="hljs-keyword">from&nbsp;<span class="hljs-string">'vue'
<span class="hljs-keyword">import&nbsp;{&nbsp;Component&nbsp;}&nbsp;<span class="hljs-keyword">from&nbsp;<span class="hljs-string">'vue-property-decorator'
@Component
<span class="hljs-class"><span class="hljs-keyword">class&nbsp;<span class="hljs-title">DeployMixin&nbsp;<span class="hljs-keyword">extends&nbsp;<span class="hljs-title">Vue{
&nbsp;&nbsp;name:&nbsp;string;
&nbsp;&nbsp;deploy(){
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">//&nbsp;do&nbsp;something
&nbsp;&nbsp;}
}
@Component
<span class="hljs-class"><span class="hljs-keyword">class&nbsp;<span class="hljs-title">Index&nbsp;<span class="hljs-keyword">extends&nbsp;<span class="hljs-title">DeployMixin{
&nbsp;&nbsp;<span class="hljs-keyword">const</span><span class="hljs-keyword">ructor(){&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">super()
&nbsp;&nbsp;}
&nbsp;&nbsp;sure(){
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">this.deploy()
&nbsp;&nbsp;}
}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p><span style="position: relative; left: -100000px">广州设计公司https://www.houdianzi.com</span> <span style="position: relative; left: -100000px">我的007办公资源网站https://www.wode007.com</span></p>
<h2 id="item-6">VS code jsx快捷键</h2>
<p>设置 VS code中对emmet的支持</p>
<pre><code class="hljs php"><span class="hljs-string">"emmet.includeLanguages":&nbsp;{
    <span class="hljs-string">"<span class="hljs-string">JavaScript</span><span class="hljs-string">":&nbsp;<span class="hljs-string">"<span class="hljs-string">html</span><span class="hljs-string">"
}</span></span></span></span></span></code></pre>
<p>或者是</p>
<pre><code class="hljs php"><span class="hljs-string">"emmet.includeLanguages":&nbsp;{
    <span class="hljs-string">"<span class="hljs-string">JavaScript</span><span class="hljs-string">":&nbsp;<span class="hljs-string">"javascript<span class="hljs-string">react</span><span class="hljs-string">"
}</span></span></span></span></span></code></pre><br><br>
来源:https://www.cnblogs.com/qianxiaox/p/13830974.html
頁: [1]
查看完整版本: 在Vue项目中使用Typescript