基于Strview.js項目腳手架StrviewApp是怎么搭建起來的?
前言
前幾天,因為借著看源碼的熱乎勁,搞了一個玩具Js庫Strview.js。為什么會搞這么一個玩具庫呢?其實也不全是因為晚上閑的沒事,主要還是想通過實操來鍛煉自己的開發(fā)能力。之前,我也寫過一篇文章,那篇文章只是大體介紹了一下,沒有深究。之前大家可能覺得它跟Vue.js差不多,是的,正是借鑒Vue.js的思想,但是有些地方還是不一樣(個人覺得)。所以,今天,這篇文章介紹基于Strview.js搭建的項目腳手架工具StrviewApp。如果你覺得對自己有用,可以繼續(xù)看下去。如果覺得這篇肯定是篇垃圾文章,你也可以避而遠(yuǎn)之。好了,我們現(xiàn)在就進(jìn)去正題。準(zhǔn)備好了嗎?一起跟我來吧!
快速上手StrviewAPP
你可以通過StrviewCLI快速初始化StrviewAPP項目,你可以這樣:
全局安裝。
npm i strview-cli -g
安裝完成之后,你可以查看版本。
strview-cli -v
最后,就是初始化項目了, <projectName>是自定義項目名稱。
strview-cli init <projectName>
or
strview-cli i <projectName>
這樣,一個StrviewAPP項目就這么搭建完成了。
StrviewAPP項目結(jié)構(gòu)
下圖就是StrviewAPP項目組織結(jié)構(gòu)。

下面,我將介紹每個文件及文件夾的作用。
config
這個是webpack配置文件夾,關(guān)于webpack的配置都在這配置。文件夾中里面有如下三個文件,分別如下:
- webpack.base.js // 基礎(chǔ)配置
- webpack.dev.js // 開發(fā)環(huán)境配置
- webpack.pro.js // 生產(chǎn)環(huán)境配置
public
資源文件夾。
- favicon.ico // 網(wǎng)站標(biāo)識
- index.html // 模板文件
.gitignore
哪些文件不需要添加到版本管理中。
.prettierrc
Prettier 規(guī)則配置文件。
package.json
定義了這個項目所需要的各種模塊,以及項目的配置信息(比如名稱、版本、許可證等元數(shù)據(jù))。
src
這個文件夾是StrviewAPP項目的主要文件夾,下面我們來看下這個文件夾里面到底有什么。
- assets //存放靜態(tài)文件
- components // 組件文件夾
- data // 公用狀態(tài)文件夾
- methods // 方法文件夾
- style // 樣式文件夾
- template // 模板文件夾
- App.js // 頁面入口
- main.js // 項目入口文件
Src文件夾詳析
上面我們分析完了項目結(jié)構(gòu),那么下面我們將進(jìn)一步分析Src文件夾中的文件構(gòu)成以及它們之間如何配合的。
1. main.js
首先,我們看下main.js文件,這是項目入口文件,我們來看下文件中的內(nèi)容。
import { createView } from 'strview';
import data from './data';
import App from './App';
import methods from './methods';
createView({
el: "#app",
template: App,
data
});
// The event is handled after the createview API
methods();
我們先引入了strview.js,導(dǎo)入createView這個API用于創(chuàng)建視圖。那么,我們我們跳到下面看下這個API是怎么使用的。首先我們傳入一個對象字面量,第一個屬性是el屬性,它是掛載的DOM節(jié)點。第二個屬性是template屬性,它是用于顯示視圖的模板。第三個屬性是data屬性,傳入值為顯示的數(shù)據(jù)。最后,我們看到有這么一行注釋The event is handled after the createview API,意思是事件方法必須要在createViewAPI之后調(diào)用,即這里的methods();。
2. App.js
上面說到,App.js用與顯示視圖的模板,那么下面我們來看下。
import myParagraph from './components/myParagraph';
import card from './components/card';
import helloTemplate from './template/helloTemplate';
import './style/index.css';
const App = `
${helloTemplate}
<div class="content">
<button class="color-red">點擊</button>
<p class="txt">{a},,(a和b都改變)</p>
<ul class="list">
<li>{age}</li>
<li>{name}</li>
<li>{msg}</li>
</ul>
<p class="txt">{a},(a會改變)</p>
<p class="txt">,(b會改變)</p>
<input value="{msg}"></input>
<p>{obj.a.b}</p>
<p>{arr}</p>
<p>{ob.name}</p>
</div>
${myParagraph}
${card}<my-card><span slot="my-card-txt"></span></my-card>
`
export default App
我們看到在代碼的末尾導(dǎo)出了一個模板字符串,也就是常量App。我們可以看到模板字符串中都是些類似標(biāo)簽語句的代碼。是的,這也是Strview.js的關(guān)鍵之處,使用含有類似標(biāo)簽語句的模板字符串來構(gòu)建視圖。
另外,我們看到頂部除了引入樣式文件,還從components文件夾引入了兩個文件,template文件夾中引入了一個文件。我們從前面目錄結(jié)構(gòu)知道,components文件夾存放的是組件,而template文件夾存放的是模板文件。如何將導(dǎo)入模板與組件呈現(xiàn)到頁面上呢?那么就需要在模板字符串中使用${}占位符。在這里你可能會感到很困惑,因為沒有看到這些文件中什么內(nèi)容,不過不要著急,我們慢慢來。你在這里只需要記住它們在這里占位就可以了。
你可能會看到<my-card></my-card>這種標(biāo)簽,你可能說沒見過!這只是一個自定義組件。具體為什么這樣寫,我們下面到組件時再分析。但是需要說明的是,如果我們組件中需要存放內(nèi)容時,我們需要在自定義組件前使用一個占位符${},如這里的${card},card是引入的組件。
最后,我們在標(biāo)簽中都會發(fā)現(xiàn)類似這種{}符號,它是用來掛載數(shù)據(jù)的,也就是為了動態(tài)更新數(shù)據(jù)的。數(shù)據(jù)這塊我們后面再細(xì)講。
3. template
上面說到,這個文件夾是存放模板文件的,我們就一探究竟。
- helloTemplate.css
- helloTemplate.js
helloTemplate.css樣式文件沒有什么好說的。
.container {
text-align: center;
margin-top: 100px;
line-height: 46px;
}
.container > img {
margin-bottom: 40px;
}
helloTemplate.js我們來看下這個js文件。
import logo from '../assets/logo.png';
import './helloTemplate.css';
export default `
<div class="container">
<img src="${logo}"/>
<h1>Hello Strview.js</h1>
</div>
`;
在上面代碼中可以看到我們頭部引入了一個圖片還有一個樣式文件,下面接著導(dǎo)出了一個模板字符串。引入的圖片呢!使用${}占位符來綁定到img標(biāo)簽上。
簡單介紹下template文件夾之后,我們下面看下components文件夾。
4. components
這個文件夾的是存放組件的,組件這個概念大家可能非常熟悉,在目前Vue、React這些前端框架中都有相應(yīng)的應(yīng)用。
我們先來看下這個文件夾的目錄結(jié)構(gòu)。
- card.js
- myParagraph.js
可以看到,有兩個js文件。
先看myParagraph.js這個文件。
customElements.define('my-paragraph',
class extends HTMLElement {
constructor() {
super();
const template = document.getElementById('my-paragraph');
const templateContent = template.content;
this.attachShadow({ mode: 'open' }).appendChild(
templateContent.cloneNode(true)
);
}
}
);
const myParagraph = `<template id="my-paragraph">
<style>
p {
color: white;
background-color: #666;
padding: 5px;
}
</style>
<p>
<slot name="my-text">My default text</slot>
</p>
</template>
<my-paragraph>
<span slot="my-text">Let's have some different text!</span>
</my-paragraph>
<my-paragraph>
<ul slot="my-text">
<li>Let's have some different text!</li>
<li>In a list!</li>
</ul>
</my-paragraph>`
export default myParagraph
我們先看上一部分,customElements對象下有一個define方法。這是什么方法呢?其實這部分利用了Web Components。它是什么呢?我們在MDN這樣定義它的。
Web Components 是一套不同的技術(shù),允許您創(chuàng)建可重用的定制元素(它們的功能封裝在您的代碼之外)并且在您的web應(yīng)用中使用它們。
Web Components拆開來講其實也挺復(fù)雜,我們在這里就不詳細(xì)分析了。以下是MDN網(wǎng)址,大家可以跟著做幾個例子。
https://developer.mozilla.org/zh-CN/docs/Web/Web_Components
我們在這里是需要知道define方法第一個參數(shù)需要傳一個自定義標(biāo)簽名,第二個參數(shù)是傳入一個類。需要自定義的地方是第一個參數(shù)與第二個參數(shù)中getElementById()方法中的參數(shù),推薦使用相同的字符串。
調(diào)用define方法完成后,你需要在下面模板字符串中首先要使用template標(biāo)簽包裹起來,你可以理解成初始化。我們可以看到在template標(biāo)簽上有一個id選擇器與上面的getElementById()方法中的參數(shù)一樣。是的,這地方必須一一對應(yīng)。另外,我們看到緊接著下面有一個style標(biāo)簽,這是定義組件樣式的。最后就是組件的內(nèi)容了。這里定義了一個p標(biāo)簽,里面是一個插槽,定義了一個name屬性。并且這里有一個標(biāo)簽文本,這個文本內(nèi)容是默認(rèn)顯示的,如果組件中沒有內(nèi)容,則這個內(nèi)容就會默認(rèn)顯示。
<template id="my-paragraph">
<style>
p {
color: white;
background-color: #666;
padding: 5px;
}
</style>
<p>
<slot name="my-text">My default text</slot>
</p>
</template>
我們接著看下面代碼,它們都是用<my-paragraph></my-paragraph>包裹起來。另外,標(biāo)簽里面則是普通的標(biāo)簽語句。但是,有一點不一樣的是,這些普通的標(biāo)簽語句都有一個slot屬性,這個屬性用于當(dāng)作插槽的模板。
<my-paragraph>
<span slot="my-text">Let's have some different text!</span>
</my-paragraph>
<my-paragraph>
<ul slot="my-text">
<li>Let's have some different text!</li>
<li>In a list!</li>
</ul>
</my-paragraph>
分析完了myParagraph.js文件,我們接著分析card.js文件。
其實與myParagraph.js文件一樣,只不過它是負(fù)責(zé)定義組件。在上面的App.js中,我們提到我們需要在自定義組件前使用一個占位符${},如這里的${card},card是引入的組件,就是指的它。
customElements.define('my-card',
class extends HTMLElement {
constructor() {
super();
const template = document.getElementById('my-card');
const templateContent = template.content;
this.attachShadow({ mode: 'open' }).appendChild(
templateContent.cloneNode(true)
);
}
}
);
const card = `<template id="my-card">
<style>
div {
color: #333;
background-color: #f4f4f4;
padding: 5px;
}
</style>
<div>
<slot name="my-card-txt"></slot>
</div>
</template>
`
export default card
5. data
這個文件夾是負(fù)責(zé)存放數(shù)據(jù)狀態(tài)的文件,里面主要有這兩個文件。
- index.js
- ob.js
我們先來看下index.js文件,非常簡單,就是單純的導(dǎo)出一個對象,另外ob.js文件也是。
index.js
import ob from './ob';
export default {
a: "Hello",
b: 18,
name: "maomin",
age: 9,
msg: 'Strview',
arr: ['0'],
obj: {
a: {
b: 1
}
},
ob
}
ob.js
export default {
name: 'kk'
}
6. methods
我們在main.js文件中中提到一點。
import methods from './methods';
// The event is handled after the createview API
methods();
就是調(diào)用這個方法。那么,我們下面看下這個methods文件夾,我們知道這個文件夾的作用是提供事件處理方法的。它的目錄結(jié)構(gòu)如下:
- index.js
- item.js
先來看下item.js這個文件。
import { reactive, ref } from 'strview'
function executes() {
reactive().obj.a.b = 3;
ref().name = 'Strview.js';
}
function useItem() {
ref().b = 100;
}
export {
executes,
useItem
}
我們可以看到在頭部引入了兩個方法,reactive、ref這兩個方法前者負(fù)責(zé)處理復(fù)雜類型的數(shù)據(jù),如數(shù)組、嵌套對象,后者處理簡單類型的數(shù)據(jù),如單一對象、原始值??梢钥吹皆谏厦娲a我們通過調(diào)用reactive()、ref()這兩個方法來實現(xiàn)數(shù)據(jù)的響應(yīng)式,然后導(dǎo)出這兩個executes()、useItem()方法。
接著,我們來看下index.js文件。
import { eventListener } from 'strview';
import { executes, useItem } from './item';
const eventList = [
['.color-red', 'click', executes],
['.list>li:nth-child(2)', 'click', useItem]
]
function methods() {
for (let index = 0; index < eventList.length; index++) {
const element = eventList[index];
eventListener(...element);
}
}
export default methods
我們首先在文件頂部引入了一個eventListener方法,然后接著從item文件夾引入的之前導(dǎo)出的兩個方法。通過定義一個數(shù)組,來不斷地循環(huán)調(diào)用eventListener方法。最后導(dǎo)出methods方法。
7. style
這個是存放樣式的文件,不過多介紹了。
index.css
* {
margin: 0;
padding: 0;
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.content {
text-align: center;
margin-top: 50px;
}
8. assets
這個文件夾存放的是靜態(tài)資源,比如圖片之類的資源。
項目啟動
初始化安裝依賴
yarn install
OR
npm install
啟動項目
yarn start
OR
npm run start
打包部署
yarn build
OR
npm run build
項目一覽

結(jié)語
謝謝你的閱讀!
這個腳手架相比于現(xiàn)在熱門的前端框架中的腳手架肯定是沒有可比性,可以當(dāng)做是玩具吧!也可以當(dāng)做自己看源碼之后自己的一些感悟吧!
關(guān)于作者
作者:Vam的金豆之路。曾獲得2019年CSDN年度博客之星,CSDN博客訪問量已達(dá)到數(shù)百萬。掘金博客文章多次推送到首頁,總訪問量已達(dá)到數(shù)十萬。
另外,我的公眾號:前端歷劫之路,公眾號持續(xù)更新最新前端技術(shù)及相關(guān)技術(shù)文章。歡迎關(guān)注我的公眾號,讓我們一起在前端道路上歷劫吧!Go!
關(guān)注它,技術(shù)之路不孤單。邀您進(jìn)群

