<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          組件化實(shí)戰(zhàn)——組件知識(shí)和基礎(chǔ)輪播組件

          共 10924字,需瀏覽 22分鐘

           ·

          2021-07-29 11:23

          點(diǎn)擊上方 前端Q,關(guān)注公眾號(hào)

          回復(fù)加群,加入前端Q技術(shù)交流群

          作者:blateyang

          原文鏈接:https://juejin.cn/post/6986304993171079176/



          1.1. 組件的基本知識(shí)

          1.1.1 前端兩大重點(diǎn)內(nèi)容

          • 組件化:解決復(fù)用問(wèn)題

          • 架構(gòu)模式:如MVC、MVVM等,解決前端和數(shù)據(jù)邏輯層的交互問(wèn)題

          1.1.1.2 組件的理解

          組件可以看作特殊的對(duì)象和模塊,它和UI是強(qiáng)相關(guān)的,是可復(fù)用的界面功能模塊。它除了具有對(duì)象的property,method,inherit之外,還有attribute,state,children,event等,下圖描述了組件組成部分間的關(guān)系

          1.1.1.3 Attribute vs Property

          • Attribute強(qiáng)調(diào)描述性

          • Property強(qiáng)調(diào)從屬關(guān)系

          在html中,二者含義是不同的

          <input value="cute">
          <script>
          var input = document.getElementByTagName("input"); // 若property沒(méi)有設(shè)置,則結(jié)果是attribute
          input.value //cute
          input.getAttribute("value"); //cute
          input.value = "hello"; //若value屬性已設(shè)置,則attribute不變,property變化,元素上實(shí)際的效果是property優(yōu)先
          input.value // hello
          input.getAttribute("value"); //cute
          </script>

          1.1.1.4 如何設(shè)計(jì)組件狀態(tài)(?表示待定)


          Markup set JS set JS Change User Input Change
          property x ?
          attribute ?
          state x x x
          config x x x

          1.1.1.5 生命周期Lifecycle

          created
          mount
          unmount
          destroyed
          JSchange/set
          render/update
          UserInput

          1.2 為組件添加jsx語(yǔ)法

          1.2.1 搭建支持jsx語(yǔ)法的環(huán)境

          jsx是babel的插件,因此要依次安裝webpack,babel-loader, babel和babel-plugin

          1. 安裝webpack,用于靜態(tài)模塊打包

          npm install -g webpack webpack-cli

          1. 安裝babel-loader,用于將其他語(yǔ)言的代碼轉(zhuǎn)譯成webpack能夠識(shí)別的語(yǔ)言(js或json)

          npm install --save-dev webpack babel-loader

          1. 安裝babel用于將新版本的js編譯成舊版本的js以便能跑在舊版本的瀏覽器中

          npm install --save-dev @babel/core @babel/preset-env

          1. 安裝react-jsx插件用于在js中能夠使用jsx

          npm install --save-dev @babel/plugin-transform-react-jsx

          1. 安裝完后還要在webpack.config.js中將安裝的各種庫(kù)填寫(xiě)進(jìn)配置文件中,如下

          module.exports = {
          entry: "./main.js",
          module: {
          rules: [
          {
          test: /\.js$/,
          use: {
          loader: "babel-loader",
          options: {
          presets: ["@babel/preset-env"],
          plugins: ["@babel/plugin-transform-react-jsx"]
          }
          }
          }
          ]
          },
          mode: "development"
          }

          1.3 JSX的基本使用方法

          1.3.1 JSX的原理

          利用babel/plugin-transform-react-jsx插件將html標(biāo)簽寫(xiě)法轉(zhuǎn)換成創(chuàng)建dom樹(shù)的函數(shù)調(diào)用createElement(type, attributes, ...children)

          1.3.2 createElement函數(shù)的基本實(shí)現(xiàn)

          function createElement(type, attributes, ...children) {
          let element = document.createElement(type)
          for(let attr in attributes) {
          element.setAttribute(attr, attributes[attr])
          }
          for(let child of children) {
          if(typeof child === "string") {
          child = document.createTextNode(child)
          }
          element.appendChild(child)
          }
          return element
          }

          1.3.3 增加對(duì)自定義標(biāo)簽的支持

          function createElement(type, attributes, ...children) {
          let element
          if(typeof type === "string")
          element = document.createElement(type)
          else
          element = new type()
          for(let attr in attributes) {
          element.setAttribute(attr, attributes[attr])
          }
          for(let child of children) {
          if(typeof child === "string") {
          child = document.createTextNode(child)
          }
          element.appendChild(child)
          }
          return element
          }

          class Div{
          constructor() {
          this.root = document.createElement("section")
          }
          setAttribute(name, value) {
          this.root.setAttribute(name, value)
          }
          appendChild(child) {
          this.root.appendChild(child)
          }
          mountTo(parent) {
          parent.appendChild(this.root)
          }
          }

          let a = <Div id="a">
          <span>a</span>
          <span>b</span>
          <span>c</span>
          </Div>

          // document.body.appendChild(a)
          a.mountTo(document.body)

          1.3.4 給原生html標(biāo)簽添加包裝類(lèi)使其支持mountTo方法

          function createElement(type, attributes, ...children) {
          let element
          if(typeof type === "string")
          element = new ElementWrapper(type)
          else
          element = new type()
          for(let attr in attributes) {
          element.setAttribute(attr, attributes[attr])
          }
          for(let child of children) {
          if(typeof child === "string") {
          child = new TextWrapper(child)
          }
          child.mountTo(element)
          }
          return element
          }


          class ElementWrapper{
          constructor(type) {
          this.root = document.createElement(type)
          }
          setAttribute(name, value) {
          this.root.setAttribute(name, value)
          }
          appendChild(child) {
          child.mountTo(this.root);
          }
          mountTo(parent) {
          parent.appendChild(this.root)
          }
          }

          class TextWrapper{
          constructor(content) {
          this.root = document.createTextNode(content)
          }
          setAttribute(name, value) {
          this.root.setAttribute(name, value)
          }
          appendChild(child) {
          child.mountTo(this.root);
          }
          mountTo(parent) {
          parent.appendChild(this.root)
          }
          }

          class Div{
          constructor() {
          this.root = document.createElement("section")
          }
          setAttribute(name, value) {
          this.root.setAttribute(name, value)
          }
          appendChild(child) {
          child.mountTo(this.root);
          }
          mountTo(parent) {
          parent.appendChild(this.root)
          }
          }


          let a = <Div id="a">
          <span>a</span>
          <span>b</span>
          <span>c</span>
          </Div>

          // document.body.appendChild(a)
          a.mountTo(document.body)

          2 動(dòng)手實(shí)現(xiàn)一個(gè)輪播組件

          class Component {
          constructor(type) {
          // this.root = this.render()
          }
          setAttribute(name, value) {
          this.root.setAttribute(name, value)
          }
          appendChild(child) {
          this.root.appendChild(child)
          }
          mountTo(parent) {
          parent.appendChild(this.root)
          }
          }

          class Carousel extends Component{
          constructor() {
          super()
          this.attributes = Object.create(null) // 創(chuàng)建空對(duì)象接收src屬性
          }

          setAttribute(name, value) { // 重寫(xiě)基類(lèi)的方法,以便將src設(shè)置到組件中
          this.attributes[name] = value
          }

          render() {
          // console.log(this.attributes.src)
          this.root = document.createElement("section")
          this.root.classList.add("carousel")
          for(let item of this.attributes.src) {
          let img = document.createElement("section")
          img.style.backgroundImage = `url(${item})`
          this.root.appendChild(img)
          }

          let currentIdx = 0
          let time = 3000
          let children = this.root.children

          // 自動(dòng)輪播, 每次只需移動(dòng)viewport中的兩張相鄰圖片
          let timer = setInterval(()=> {
          let nextIdx = (currentIdx + 1) % children.length

          let current = children[currentIdx]
          let next = children[nextIdx]
          // next快速移入viewport的后一個(gè)位置
          next.style.transition = "none"
          next.style.transform = `translateX(${100 - nextIdx*100}%)`

          setTimeout(()=>{// 實(shí)現(xiàn)current移出viewport,next移入viewport且next快速切換到current
          next.style.transition = "" // 恢復(fù)樣式表中的transition設(shè)置
          current.style.transform = `translateX(${-100 - currentIdx*100}%)`
          next.style.transform = `translateX(${-nextIdx*100}%)`
          currentIdx = nextIdx
          }, 16) // 此處設(shè)置延時(shí)是為了避免立即覆蓋前面對(duì)next的設(shè)置
          }, time)

          // 手動(dòng)輪播
          let position = 0
          this.root.addEventListener("mousedown", (event) => {
          let startX = event.clientX
          console.log("mousedown")
          let move = (event)=>{
          console.log("mousemove")
          let x = event.clientX - startX
          let current = position
          for(let offset of [-1, 0, 1]) {
          let pos = current + offset
          pos = (pos + children.length) % children.length // 將索引-1變?yōu)?
          children[pos].style.transition = "none" // 拖動(dòng)時(shí)關(guān)閉過(guò)渡效果
          children[pos].style.transform = `translateX(${-pos*500 + offset*500 + x%500}px)`
          }
          }
          let up = (event) => {
          let x = event.clientX - startX
          let current = position - Math.round(x/500) // 獲取松手時(shí)就近的幀索引
          for(let offset of [0, Math.sign(Math.abs(x)>250 ? x:-x)]) {
          // 拖動(dòng)距離大于視口的一半,當(dāng)前圖片和下一張圖片跟著移動(dòng),否則當(dāng)前圖片和上一張圖片跟著移動(dòng)
          let pos = current + offset
          pos = (pos + children.length) % children.length // 將索引-1變?yōu)?
          children[pos].style.transition = "" // 恢復(fù)過(guò)渡效果
          children[pos].style.transform = `translateX(${-pos*500 + offset*500}px)`
          }
          console.log("mouseup")
          document.removeEventListener("mousemove", move)
          document.removeEventListener("mouseup", up)
          }
          // 在document上監(jiān)聽(tīng)可以防止移出圖片區(qū)域無(wú)法響應(yīng)監(jiān)聽(tīng)事件
          document.addEventListener("mousemove", move)
          document.addEventListener("mouseup", up)
          })
          return this.root
          }

          mountTo(parent) {
          parent.appendChild(this.render())
          }
          }
          let d = ['https://res.devcloud.huaweicloud.com/obsdevui/diploma/8.1.17.002/banner-8.d14241daf518717981c6.jpg',
          'https://res.devcloud.huaweicloud.com/obsdevui/diploma/8.1.17.002/banner-1.fdbf82d4c2ca7fad3225.jpg',
          'https://res.devcloud.huaweicloud.com/obsdevui/diploma/8.1.17.002/banner-2.64b1407e7a8db89d6cf2.jpg',
          'https://res.devcloud.huaweicloud.com/obsdevui/diploma/8.1.17.002/banner-3.ce76c93c7a8a149ce2a2.jpg']
          let a = <Carousel src=go7utgvlrp/>
          a.mountTo(document.body)

          注:自動(dòng)輪播中下面兩行代碼目的是在每一次輪播時(shí)提前將下一張輪播圖移到viewport右側(cè),以便在視覺(jué)上能夠形成輪播的效果,如下圖所示,a,b,c,d是四張圖,每一行代表transform后的一次狀態(tài),虛線(xiàn)箭頭表示transition為none時(shí)的移動(dòng)過(guò)程

                next.style.transition = "none"
          next.style.transform = `translateX(${100 - nextIdx*100}%)`


          ps:如果覺(jué)得此文對(duì)你有幫助或啟發(fā),請(qǐng)順手點(diǎn)贊和分享,這是對(duì)我的最大鼓勵(lì),如有疑問(wèn),請(qǐng)留言或私信交流,看到會(huì)及時(shí)回復(fù)



          內(nèi)推社群


          我組建了一個(gè)氛圍特別好的騰訊內(nèi)推社群,如果你對(duì)加入騰訊感興趣的話(huà)(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行面試相關(guān)的答疑、聊聊面試的故事、并且在你準(zhǔn)備好的時(shí)候隨時(shí)幫你內(nèi)推。下方加 winty 好友回復(fù)「面試」即可。


          瀏覽 43
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  这里只有免费精品6 | 免费看男女操逼的网站 | 综合+夜夜| 黄色小网站在线观看 | 中文有码在线观看 |