手把手教你使用webpack打包前端組件(二)

這是我參與更文挑戰的第8天,活動詳情查看: 更文挑戰javascript

回顧前面

在上一篇文章中,咱們講述了什麼是組件,與開發一個組件須要用到些什麼工具,以後又帶領着你們把原有的webpack-template的目錄結構和配置文件進行了一些小改動,接下來這一期將把組件引進到模板中以及如何去調試配置咱們的組件 ~ ~css

若是尚未閱讀第一篇的小夥伴們,請點擊閱讀手把手教你使用webpack打包前端組件(一)html

導入插件

回到咱們在上一篇文章中建立的src / components / better-draggable-ball / index.ts文件,把以前寫好的插件代碼粘貼入進去前端

images (1).jpg

// 插件代碼
interface DefaultPositionType {
  x?: number,
  y?: number
}
interface Options {
  autoAdsorbent?: boolean;

  hideOffset?: number;

  defaultPosition?: DefaultPositionType;
}

export default class Drag {
  // 元素
  element: HTMLElement;

  // 屏幕尺寸
  screenWidth: number;

  screenHeight: number;

  // 元素大小
  elementWidth: number;

  elementHeight: number;

  isPhone: boolean;

  // 當前元素座標
  elementX: number;

  elementY: number;

  // 元素offset
  elementOffsetX: number;

  elementOffsetY: number;

  // 是否處於拖動狀態
  moving: boolean;

  // 吸附
  autoAdsorbent: boolean;

  // 隱藏
  hideOffset: number;

  constructor(element: HTMLElement, dConfig: Options = {}) {
    dConfig = this.InitParams(dConfig);
    this.element = element;
    this.screenWidth = window.innerWidth || window.outerWidth || 0;
    this.screenHeight = window.innerHeight || window.outerHeight || 0;
    this.elementWidth = this.element.offsetWidth || 0;
    this.elementHeight = this.element.offsetHeight || 0;
    this.isPhone = /(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent);
    this.element.style.position = 'absolute';
    this.elementX = 0;
    this.elementY = 0;
    this.elementOffsetX = 0;
    this.elementOffsetY = 0;
    this.moving = false;
    this.autoAdsorbent = dConfig.autoAdsorbent;
    this.hideOffset = this.elementWidth * dConfig.hideOffset;
    if (!this.isPhone) {
      console.error('警告!!當前插件版本只兼容移動端');
    }
    // 默認位置
    this.setElementPosition(dConfig.defaultPosition.x, dConfig.defaultPosition.y);
    this.watchTouch();
  }

  protected InitParams(dConfig: Options):Options {
    // 處理下Options未配置的參數
    return {
      autoAdsorbent: dConfig.autoAdsorbent || false,
      hideOffset: dConfig.hideOffset || 0,
      defaultPosition: dConfig.defaultPosition || { x: 0, y: 0 },
    };
  }

  private watchTouch(): void {
    this.element.addEventListener('touchstart', (event: TouchEvent) => {
      const rect = (event.target as HTMLElement).getBoundingClientRect();
      // 頁面被捲去的高度
      // 不兼容IE
      const docScrollTop = document.documentElement.scrollTop;
      this.elementOffsetX = event.targetTouches[0].pageX - rect.left;
      this.elementOffsetY = event.targetTouches[0].pageY - rect.top - docScrollTop;
      this.moving = true;
      this.element.addEventListener('touchmove', this.move.bind(this), { passive: false });
    });
    window.addEventListener('touchend', () => {
      this.moving = false;
      document.removeEventListener('touchmove', this.move);
      if (this.autoAdsorbent) this.adsorbent();
    });
  }

  private setElementPosition(x: number, y: number): void {
    // 溢出處理
    // 溢出範圍
    // 但頁面超出屏幕範圍,計算當前屏幕範圍
    const leftScope = this.moving ? 0 : 0 - this.hideOffset;
    // 當前屏幕right最大值
    const rs = this.screenWidth - this.elementWidth;
    const rightScope = this.moving ? rs : rs + this.hideOffset;
    const bottomScope = this.screenHeight - this.elementHeight;
    if (x <= leftScope && y <= 0) {
      [x, y] = [leftScope, 0];
    } else if (x >= rightScope && y <= 0) {
      [x, y] = [rightScope, 0];
    } else if (x <= leftScope && y >= bottomScope) {
      [x, y] = [leftScope, bottomScope];
    } else if (x >= rightScope && y >= bottomScope) {
      [x, y] = [rightScope, bottomScope];
    } else if (x > rightScope) {
      x = rightScope;
    } else if (y > bottomScope) {
      y = bottomScope;
    } else if (x <= leftScope) {
      x = leftScope;
    } else if (y <= 0) {
      y = 0;
    }
    this.elementX = x;
    this.elementY = y;
    this.element.style.top = `${y}px`;
    this.element.style.left = `${x}px`;
  }

  private move(event: TouchEvent): void {
    event.preventDefault();
    if (!this.moving) return;
    this.elementY = (event.touches[0].pageX - this.elementOffsetX);
    this.elementX = (event.touches[0].pageY - this.elementOffsetY);
    const ex = (event.touches[0].pageX - this.elementOffsetX);
    const ey = (event.touches[0].pageY - this.elementOffsetY);
    this.setElementPosition(ex, ey);
  }

  private animate(targetLeft: number, spd: number): void {
    const timer = setInterval(() => {
      let step = (targetLeft - this.elementX) / 10;
      // 對步長進行二次加工(大於0向上取整,小於0向下取整)
      step = step > 0 ? Math.ceil(step) : Math.floor(step);
      // 動畫原理: 目標位置 = 當前位置 + 步長
      const x = this.elementX + step;
      this.setElementPosition(x, this.elementY);
      // 檢測緩動動畫有沒有中止
      if (Math.abs(targetLeft - this.elementX) <= Math.abs(step)) {
        // 處理小數賦值
        const xt = targetLeft;
        this.setElementPosition(xt, this.elementY);
        clearInterval(timer);
      }
    }, spd);
  }

  private adsorbent():void {
    // 判斷吸附方向
    // 屏幕中心點
    const screenCenterY = Math.round(this.screenWidth / 2);
    // left 最大值
    const rightScope = this.screenWidth - this.elementWidth;
    // 根據中心點來判斷吸附方向
    if (this.elementX < screenCenterY) {
      this.animate(0 - (this.hideOffset), 10);
    } else {
      this.animate(rightScope + (this.hideOffset), 10);
    }
  }
}
複製代碼

開發調試

在組件開發的過程當中每每少不了對組件的功能調試以及樣式的展現,這時可使用webpack-dev-server 這個插件,它給咱們提供了一個基本的web server,而且具備實時從新更新頁面的功能。java

安裝 webpack-dev-servernode

npm install --save-dev webpack-dev-server
複製代碼

!這裏只是先安裝,稍後咱們將在配置文件中配置使用它webpack

咱們在pages文件夾下新建Drag的頁面以及它的tsscss文件,用來調試組件:程序員

7.png

Drag頁面內容分別爲:web

// Drag.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Drag</title>
</head>
<body>
    <h2>
        Drag.html
    </h2>
</body>
</html>
複製代碼
// Drag.scss
*{
  margin: 0;
  padding: 0;
}
body{
  padding: 20px;
}
#drag{
  width: 50px;
  height: 50px;
  background-color: rgb(238, 238, 238);
  border-radius: 50%;
  border: 5px solid rgb(170, 170, 170);
}
p{
  height: 50px;
}
複製代碼
//Drag.ts
import './Drag.scss';
import Drag from '../../components/better-draggable-ball/index';

const dragDom = document.createElement('div');
dragDom.setAttribute('id', 'drag');
const body = document.getElementsByTagName('body')[0];
body.appendChild(dragDom);
new Drag(dragDom, {
  defaultPosition: { x: 10, y: 10 },
  autoAdsorbent: true,
});
複製代碼

把項目的根目錄下的webpack.config.ts文件複製多一份出來,命名爲webpack.config.dev.ts,這個文件只要用於調試時使用。typescript

修改webpack.config.dev.ts文件:

在配置類型方面,咱們須要作出一些修改,本來咱們的配置對象模塊中用的是webpack包中的config類型,但如今咱們須要用到另一個模塊(webpack-dev-server)要在配置對象中配置devServer屬性,而webpack中的config中沒有devServer這個類型的屬性,咱們定義一個Configuration接口做爲配置文件的類型,讓它繼承下webpack包中的config,當它底下有devServer的時候則對應上WebpackDevServerConfiguration

// webpack.config.dev.ts
import { Configuration as webpackConfiguration } from 'webpack';
import {Configuration as WebpackDevServerConfiguration} from 'webpack-dev-server';

interface Configuration extends webpackConfiguration{
  devServer ?: WebpackDevServerConfiguration;
}
複製代碼

加入devServer屬性,配置運行目錄和服務接口,這裏compress指的是代碼是否啓用GZIP壓縮:

const config: Configuration = {
	// 忽略一些代碼
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true,
    port: 9000,
 	 },
  }
複製代碼

entryplugins屬性中新增Drag頁面

// webpack.config.dev.ts
entry: {
	// 忽略一些代碼
    Drag: './src/pages/Drag/Drag.ts', // Drag頁面
  },
複製代碼
// webpack.config.dev.ts
new HtmlWebpackPlugin({
      title: 'Drag',
      filename: 'Drag.html',
      template: './src/pages/Drag/Drag.html',
      chunks: ['Drag', 'main'],
    }),
複製代碼

修改package.json文件:

--config 參數是指定配置文件,若是沒有指定默認是使用webpack.config.ts文件

--open 參數是當服務啓動完畢後,自動的將目標URL打開

"scripts": {
    " ... 這裏忽略了一些命令 ... "
    "serve": "webpack serve --config webpack.dev.config.ts --open"
  },
複製代碼

爲了方便你們CVCVCV大法,我直接把整個webpack.config.dev.ts貼上來哈哈,懶是程序員第一輩子產力。

0165e6d206f64926b1a1653644856f8f.jpg

// webpack.config.dev.ts
import * as path from 'path';
import { Configuration as webpackConfiguration } from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import ESLintPlugin from 'eslint-webpack-plugin';
import {Configuration as WebpackDevServerConfiguration} from 'webpack-dev-server';
interface Configuration extends webpackConfiguration{
  devServer ?: WebpackDevServerConfiguration;
}
const config: Configuration = {
  mode: 'production',
  entry: {
    main: './src/main.ts',
    index: './src/pages/index/index.ts', // index頁面
    Drag: './src/pages/Drag/Drag.ts', // hello頁面
    'better-draggable-ball': './src/components/better-draggable-ball/index.ts', // better-draggable-ball 插件
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: (pathData:any) => (pathData.chunk.name === 'better-draggable-ball' ? 'js/components/[name]/[name].js' : 'js/[name]/[name].js'),
    clean: true,
  },
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true,
    port: 9000,
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          // 將 JS 字符串生成爲 style 節點
          'style-loader',
          // 將 CSS 轉化成 CommonJS 模塊
          'css-loader',
          // 將 Sass 編譯成 CSS
          'sass-loader',
        ],
      },
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource',
      },
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'index',
      filename: 'index.html',
      template: './src/pages/index/index.html',
      chunks: ['index', 'main'],
    }),
    new HtmlWebpackPlugin({
      title: 'Drag',
      filename: 'Drag.html',
      template: './src/pages/Drag/Drag.html',
      chunks: ['Drag', 'main'],
    }),
    new ESLintPlugin({
      extensions: ['js', 'ts'],
      exclude: '/node_modules/',
    }),

  ],
};

export default config;
複製代碼

執行npm run serve後,webserver將開始運行構建一個服務環境,對應的URL地址也在terminal中顯示出來,webserver也會自動的幫咱們打開瀏覽器訪問對應的URL地址。

8.png

當這不是咱們想要Drag頁面,如今有兩種方法能夠切換到Drag頁面中:

  • 在瀏覽器中把URL路徑修改成http://localhost:8080/Drag.html

    • 不推薦這種作法,當你每次啓動服務以後,你須要手動去修改它
  • devserver對象中,添加openPage屬性,讓其頁面自動的顯示出來

    • devServer: {
      	openPage: 'Drag.html',
      }
      複製代碼

再跑一下,瀏覽器自動的打開Drag頁面,咱們的better-drag-ball組件也顯示了出來。

9.gif

最後

到了這裏,咱們的組件的調試環境已經部署好了。

感謝你們的觀看下一篇文章中咱們將這個組件進行一個多版本的輸出打包,讓用戶直接引用javascript文件就可使用該組件。

😀😀 關注我,不迷路! 😀😀

相關文章
相關標籤/搜索