Hexo Basic

Hexo TocHelper 錯誤及其修正

問題

Hexo的TocHelper生成的目錄,其中的連結無法導向文章的標題,是出了什麼問題?

首先我們去看那個元素,發現這個a缺少了href。

1
<a class="toc-link"><span class="toc-text">Using Firebase via CDN</span></a>

解決

首先,我們搜尋toc這個關鍵字,在node_modules\hexo\lib\plugins\helper\toc.js找到function tocHelper ,就是我們要修正的目標。

可以看到,a.toc-link的href若無,則會生成沒有href的a

1
2
3
4
5
if (href) {
result += `<a class="${className}-link" href="${href}">`;
} else {
result += `<a class="${className}-link">`;
}

從這裡可以知道,href是由id作成的,而id是來自於el,el則是data的item

1
2
3
4
5
6
for (let i = 0, len = data.length; i < len; i++) {
const el = data[i];
const { level, id, text } = el;
const href = id ? `#${encodeURL(id)}` : null;
//...
}

我們試著把data Print出來,注意,不是在瀏覽器,而是在你的node terminal。

1
2
3
4
5
6
7
console.log(data)
for (let i = 0, len = data.length; i < len; i++) {
const el = data[i];
const { level, id, text } = el;
const href = id ? `#${encodeURL(id)}` : null;
//...
}

在for之前的data是長這樣的,可以發現,id是 "",也就是空白字串,而text則是我們可以拿來用的東西

1
2
3
4
5
6
7
8
9
10
[
{ text: '使用Firebase', id: '', level: 1 },
{ text: '前言', id: '', level: 1 },
{ text: '安裝', id: '', level: 1 },
{ text: 'Using Firebase via CDN', id: '', level: 2 },
{ text: 'Using Firebase via NPM', id: '', level: 2 },
{ text: '實作', id: '', level: 1 },
{ text: '在Angular使用Firebase', id: '', level: 2 },
{ text: '自訂FirebaseAuth介面', id: '', level: 2 }
]

在hexo html的id生成時,若遇到空格,會自動加上-,然後變成小寫。例如 'Using Firebase via CDN' 這個文字要轉成id,會變成 using-firebase-via-cdn,我們就依照這個原則去寫一個方程式將text轉成可用的id。

在下方,我寫了一個 encodedText function去修正text,生出來的id就是符合上述規範,要把它改成href的話,只要在前面加上 # 即可。

1
2
3
4
5
6
7
8
9
10
11
12
for (let i = 0, len = data.length; i < len; i++) {
let encodedText = function(txt){
let new_txt = String(txt).toLowerCase().replace(/ /g, "-");
console.log(new_txt)
return new_txt;
}
const el = data[i];
const { level, id, text } = el;
// const href = id ? `#${encodeURL(id)}` : null;
const href = text ? `#${encodedText(text)}` : null;
//...
}

以下是成功修正後的a

1
<a class="toc-link" href="#using-firebase-via-cdn"><span class="toc-text">Using Firebase via CDN</span></a>

問題解決。

閱讀全文

Angular 與 Firebase

使用Firebase

前言

目前使用版本: 8.2.5

官方文件: https://firebase.google.com/docs/auth/web/firebaseui

安裝

Using Firebase via CDN

./src/index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- Insert these scripts at the bottom of the HTML, but before you use any Firebase services -->
<!-- Firebase App (the core Firebase SDK) is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/8.2.5/firebase-app.js"></script>

<!-- If you enabled Analytics in your project, add the Firebase SDK for Analytics -->
<script src="https://www.gstatic.com/firebasejs/8.2.5/firebase-analytics.js"></script>

<!-- Add Firebase products that you want to use -->
<script src="https://www.gstatic.com/firebasejs/8.2.5/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.2.5/firebase-firestore.js"></script>

<script src="https://www.gstatic.com/firebasejs/ui/4.6.1/firebase-ui-auth.js"></script>
<link type="text/css" rel="stylesheet" href="https://www.gstatic.com/firebasejs/ui/4.6.1/firebase-ui-auth.css" />

新建一個config檔案: src/app/xxx_config.ts

1
2
3
4
5
6
7
8
9
10
export const firebaseConfig:object = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_PROJECT_ID.firebaseapp.com",
databaseURL: "https://YOUR_PROJECT_ID.firebaseio.com",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_PROJECT_ID.appspot.com",
messagingSenderId: "SENDER_ID",
appId: "APP_ID",
measurementId: "G-MEASUREMENT_ID",
};

./src/app/logging-page/logging-page.component.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { firebaseConfig } from '../xxx_config';

export class LoggingPageComponent implements OnInit {

constructor() {}

@Input() firebaseConfig: object;

ngOnInit(): void {
const firebase = globalThis.firebase;
const firebaseui = globalThis.firebaseui;
firebase.initializeApp(firebaseConfig);
var ui = new firebaseui.auth.AuthUI(firebase.auth());
//...
var uiConfig = {
//...
signInSuccessUrl: '/setting',
}
ui.start('#firebaseui-auth-container', uiConfig);

}

}

./src/app/logging-page/logging-page.component.html

1
<div id="firebaseui-auth-container" class="self-center"></div>

Using Firebase via NPM

1
$ npm install firebaseui --save
1
2
var firebase = require('firebase');
var firebaseui = require('firebaseui');

實作

在Angular使用Firebase

1
2
$ npm install firebase --save
$ npm install firebaseui --save

在需要使用auth的component中引入firebase模組

1
2
import firebase from 'firebase/app';
import * as firebaseui from 'firebaseui';

./src/app/logging-page/logging-page.component.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import firebase from 'firebase/app';
import * as firebaseui from 'firebaseui';
import { firebaseConfig } from '../xxx_config';

export class LoggingPageComponent implements OnInit {

constructor() {}

@Input() firebaseConfig: object;

ngOnInit(): void {
// const firebase = globalThis.firebase;
// const firebaseui = globalThis.firebaseui;
firebase.initializeApp(firebaseConfig);
var ui = new firebaseui.auth.AuthUI(firebase.auth());
//...
var uiConfig = {
//...
signInSuccessUrl: '/setting',
}
ui.start('#firebaseui-auth-container', uiConfig);

}

}

將firebaseui.css建立在 ./src/assets/ 下,才能夠直接被Angular的index.html引用

./src/index.html

1
<link rel="stylesheet" href="assets/firebaseui.css">

自訂FirebaseAuth介面

由於Angular在component中的scss會編譯為scoped css,會抓不到像是 .firebasui-container這樣的元件,因此要將code寫在styles.scss這個檔案中,才會被編譯為全域css。

./src/styles.scss

1
2
3
4
5
#firebaseui-auth-container{
.firebaseui-container {
box-shadow: none;
}
}
閱讀全文

Angular Basic

官方文件: https://angular.io/guide/setup-local

1
npm install -g @angular/cli
1
ng new account-manager

從AngularJS到Angular

https://angular.io/guide/ajs-quick-reference

Angular 詞彙表 https://angular.tw/guide/glossary

設定Routing

./src/app/app-routing.module.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { Component1 } from './component1/component1.component';
import { Component2 } from './component2/component2.component';

const routes: Routes = [
{ path: '', component: Component1 },
{ path: 'component2', component: Component2 },
{ path: '**', redirectTo: '/', pathMatch: 'full' },
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

其中的const routes就是用來自訂網址導向哪個component的設定

1
2
3
4
5
6
const routes: Routes = [
{ path: '', component: Component1 },
{ path: 'component2', component: Component2 },
{ path: '**', redirectTo: '/', pathMatch: 'full' },
];

./src/app/app.component.html

1
<router-outlet></router-outlet>

使用HTTPClient

1
import { HttpClient } from '@angular/common/http';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export class Component2 implements OnInit {

constructor(private http: HttpClient) {}

get_data = function():void{
this.http
.get("http://localhost:3000/data",
{observe:'body', responseType: 'json'})
.subscribe((data)=>{
this.data = data;

// console.log(this.data)
})
}

}


設定伺服器導回根目錄

問題: 當build後用http-server或live-server卻無法開啟 /component2 之類的網址?

解答:

用http-server或live-server開啟 /index.html 是沒問題的,但是當它找不到 /component2/index.html 時,就會報錯。因此要設定在找不到時,回到根目錄找尋/index.html

在官方文件中也有提到,Routed apps must fallback to index.html

https://angular.io/guide/deployment#routed-apps-must-fallback-to-indexhtml

以live-server為例,在cmd中加入 --entry-fire=./index.html

1
live-server --entry-file=./index.html

或是安裝angular-http-server,然後在cmd中指定其根目錄

1
angular-http-server --path ./dist

控制build部署時的URL

問題: 當build且deploy至有handler的伺服器時找不到main.js?

解答:

例如,輸出的資料夾為 /dist,則 /dist/index.html 所預設的是在同一資料夾裡的 /dist/main.js,連結則為 main.js。當handler想要避免把檔案都塞在根目錄下,而是放在 /SOMEWHERE/ 下時,要如何將路徑從main.js修改為/SOMEWHERE/main.js呢?

答案是,在ng build後方加上參數 --deploy-url=/SOMEWHERE/

更多參數: https://angular.io/cli/build

使用RxJS設定Loading的計時器

問題: 如何用RxJS來設定Loading圖示的計時器?

解答:

首先,引入rxjs的 Subject,以及將會使用到的operator: debounceTimetap

1
2
import { Subject } from 'rxjs';
import { debounceTime, tap } from 'rxjs/operators';

Subject是一種可以被多方監聽的Observable。

A Subject is like an Observable, but can multicast to many Observers. Subjects are like EventEmitters: they maintain a registry of many listeners.

https://rxjs-dev.firebaseapp.com/guide/subject

首先,建立一個Subject的實例,把它叫做is_editing_xxx$。請注意結尾是用 $ 來提示這是一個Subject。

我們在ngOnInit中呼叫這個Subject,讓它執行一個pipe,其中先啟動Loading圖示,經過1000毫秒後,關閉Loading圖示。然後我們訂閱這個Subject。

接著,在需要使用這個Subject的地方,例如click_sth中,我們寫下 this.is_editing_xxx$.next(),就會讓它執行下一步。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
is_editing_xxx$ = new Subject<string>();

ngOnInit(){
this.is_editing_xxx$.pipe(
tap(() => { this.loading_userconfig = true; }),
debounceTime(1000),
tap(() => {
this.loading_userconfig = false;
})
).subscribe();

}

click_sth(){
this.is_editing_xxx$.next();
}

另一種寫法是創造一個變數 is_editing_xxx,注意,這個變數的結尾沒有$的符號,如此一來,我們不需要在ngOnInit中,就可以設定 is_editing_xxx$ 的pipe和訂閱它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
is_editing_xxx$ = new Subject<string>();

is_editing_xxx = this.is_editing_xxx$
.pipe(
tap(() => { this.loading_userconfig = true; }),
debounceTime(1000),
tap(() => {
this.loading_userconfig = false;
}))
.subscribe();

ngOnInit(){
}

click_sth(){
this.is_editing_xxx$.next();
}
閱讀全文

Angular 與 MaterialUI

Angular Material官方文件

https://material.angular.io/

Using Material UI via CLI

1
$ ng add @angular/material

自訂主題

https://material.angular.io/guide/theming

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// MATERIAL-UI
@import '~@angular/material/theming';
@include mat-core();


$mat-cyan-qs: (
50: #e0f7fa,
100: #b2ebf2,
200: #80deea,
300: #4dd0e1,
400: #26c6da,
500: #2EC5CE,
600: #00acc1,
700: #0097a7,
800: #00838f,
900: #006064,
A100: #84ffff,
A200: #18ffff,
A400: #00e5ff,
A700: #00b8d4,
contrast: (
50: $dark-primary-text,
100: $dark-primary-text,
200: $dark-primary-text,
300: $dark-primary-text,
400: $dark-primary-text,
500: $light-primary-text,
600: $light-primary-text,
700: $light-primary-text,
800: $light-primary-text,
900: $light-primary-text,
A100: $dark-primary-text,
A200: $dark-primary-text,
A400: $dark-primary-text,
A700: $dark-primary-text,
)
);

// $candy-app-primary: mat-palette($mat-indigo);
$candy-app-primary: mat-palette($mat-cyan-qs);
$candy-app-accent: mat-palette($mat-indigo);

// The warn palette is optional (defaults to red).
$candy-app-warn: mat-palette($mat-red);

// Create the theme object. A theme consists of configurations for individual
// theming systems such as `color` or `typography`.
$candy-app-theme: mat-light-theme((
color: (
primary: $candy-app-primary,
accent: $candy-app-accent,
warn: $candy-app-warn,
)
));

// Include theme styles for core and each component used in your app.
// Alternatively, you can import and @include the theme mixins for each component
// that you are using.
@include angular-material-theme($candy-app-theme);


使用Mat-Dialog

1
$ ng g c ./dialog/dialog-edit-member
1
2
3
4
5
6
7
8
9
10
11
12
13
14
open_dialog_edit_member(member) {
this.member_on_editing = JSON.parse(JSON.stringify(member));
const dialogRef = this.dialog.open(DialogEditMemberComponent, {
width: "400px",
height: "300px",
data: {
member_on_editing: this.member_on_editing,
member: member,
}
});
dialogRef.afterClosed().subscribe(result => {
console.log('The dialog was closed');
});
}
閱讀全文

TailwindCSS Basic

官方文件: https://tailwindcss.com/docs/installation

Using Tailwind via NPM

1
npm install tailwindcss@latest postcss@latest autoprefixer@latest
1
npx tailwindcss init

./tailwind.config.js

1
2
3
4
5
6
7
8
9
10
11
12
// tailwind.config.js
module.exports = {
purge: {
// enabled: true,
content: [
'./src/**/*.html',
'./src/**/*.ts',
'./projects/**/*.html',
'./projects/**/*.ts'
]
},
}

其中的purge,要把檢查的html,ts都加入,讓tailwindcss在壓縮時可以檢查。開發時先把enable設為false(預設也是false),等到發佈產品前再做全面檢測及壓縮,大約可以把tailwind.css從3.7MB壓縮至10KB。

1
2
3
4
5
6
7
8
9
purge: {
// enabled: true,
content: [
'./src/**/*.html',
'./src/**/*.ts',
'./projects/**/*.html',
'./projects/**/*.ts'
]
},

將tailwind.css建立在 ./src/assets/ 下,才能夠直接被Angular引用

1
npx tailwindcss-cli@latest build -o ./src/assets/tailwind.css

./src/index.html

1
<link rel="stylesheet" href="assets/tailwind.css">

./package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"scripts": {
"ng": "ng",
"start": "ng serve",
"tw_start": "npm run build_tailwind_dev && ng serve",
"ats": "angular-http-server -p 9000 --path ./dist",
"ls": "cd ./dist && live-server --port=9000 --entry-file=./index.html",
"build": "ng build --prod && npm run build_tailwind_prod",
"build_ls": "npm run build && npm run ls",
"build_deploy": "ng build --prod --deploy-url /static/dist/ && npm run build_tailwind_prod",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"build_tailwind_prod": "npx tailwindcss-cli@latest build ./src/tailwind_src.css -c ./tailwind_pro.config.js -o ./dist/assets/css/tailwind.css",
"build_tailwind_dev": "npx tailwindcss-cli@latest build ./src/tailwind_src.css -c ./tailwind_dev.config.js -o ./src/assets/css/tailwind.css"
},

自訂tailwind.config.js

自訂顏色:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module.exports = {
theme: {
extend: {
colors: {
transparent: 'transparent',
current: 'currentColor',
indigo: {
light: '#211EAD',
DEFAULT: '#21156D',
dark: '#21156D',
},
green: {
light: '#2EC5CE'
}
}
},
}
}

開啟DarkMode:

1
2
3
4
5
module.exports = {
// darkMode: false, // or 'media' or 'class'
darkMode: 'class', // or 'media' or 'class'
}

1
2
npx tailwindcss-cli@latest build ./src/tailwind.css -c ./tailwind_pro.config.js -o ./dist/tailw 
ind.css

使用不同的tailwind_XXX.config.js

在tailwind.config.js之外,我們再建立2個config檔,分別取名為 tailwind_dev.config.js tailwind_pro.config.js ,作為開發版及產品版所使用的config檔。

首先最重要的差異在於要不要啟用 purge 功能,在開發時,我們會使用到新的utility-class,因此若啟用purge功能,會造成開發上的麻煩,很多utility-class都會被清掉而無作用。相對地,在產品版中,我們希望tailwind.css的檔案愈小愈好。

以目前使用到的功能(含暗黑模式),未壓縮的tailwind.css約為5.7MB,壓縮後的tailwind.css則為14KB,檔案大小相差極巨。

開發版的config: ./tailwind_dev.config.js

1
2
3
4
5
6
7
8
9
10
module.exports = {
purge: {
// enabled: true,
content: [
'./src/**/*.html',
'./src/**/*.ts',
'./projects/**/*.html',
'./projects/**/*.ts'
]
},

產品版的config: ./tailwind_pro.config.js

1
2
3
4
5
6
7
8
9
10
module.exports = {
purge: {
enabled: true,
content: [
'./src/**/*.html',
'./src/**/*.ts',
'./projects/**/*.html',
'./projects/**/*.ts'
]
},

以上兩者的主要差異在於 enabled: true 這行,若是comment的狀態,則預設是關閉的。

修改package.json腳本

在package.json中,我們新增一個腳本叫做protw,負責編譯產品版的tailwind.css:

1
2
"protw": "npx tailwindcss-cli@latest build ./src/tailwind_src.css -c ./tailwind_pro.config.js -o ./dist/account-manager/assets/tailwind.css",

這段指令是用npx建置dist的tailwind.css的指令。指令分解如下::

1
2
3
4
動作: npx tailwindcss-cli@latest build
來源檔: ./src/tailwind_src.css
使用的config: -c ./tailwind_pro.config.js
目的檔: -o ./dist/account-manager/assets/tailwind.css

回到package.json中,我們將build腳本加上 && npm run protw :

1
"build": "ng build && npm run protw",

其中的ng build是Angular建置dist版本的指令,而npm run protw是編譯產品版的tailwind.css,這樣就可以用npm run build 同時完成產品版的建置。

回到package.json中,我們新增一個腳本叫做devtw,負責編譯開發版的tailwind.css:

1
"devtw": "npx tailwindcss-cli@latest build ./src/tailwind_src.css -c ./tailwind.config.js -o ./src/assets/tailwind.css"
1
2
3
4
動作: npx tailwindcss-cli@latest build
來源檔: ./src/tailwind_src.css
使用的config: -c ./tailwind_dev.config.js
目的檔: -o ./src/assets/tailwind.css

修改後的package.json腳本如下:

1
2
3
4
5
6
7
8
9
10
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build && npm run protw",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"protw": "npx tailwindcss-cli@latest build ./src/tailwind_src.css -c ./tailwind_pro.config.js -o ./dist/account-manager/assets/tailwind.css",
"devtw": "npx tailwindcss-cli@latest build ./src/tailwind_src.css -c ./tailwind.config.js -o ./src/assets/tailwind.css"
},
閱讀全文

Webpack Basic

Webpack

安裝webpack

1
$ npm install webpack --save-dev

新增webpack.config.js

在根資料夾下,新增一個檔案webpack.config.js

在config指定entry及output

webpack.config.js將entry指定為index.js;output指定為bundle.js

webpack.config.js

1
2
3
4
5
6
7
8
9
10
const path = require('path');

module.exports = {
entry: './src/js/index.js',
output: {
path: path.resolve(__dirname, 'dist/js'),
filename: 'bundle.js'
},
mode: 'development'
};

./代表現在的資料夾

../代表上一層資料夾

建立指令dev(在package.json)

package.json 新增指令 "dev": "webpack"

package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"name": "XXXX",
"version": "1.0.0",
"description": "XXXX project",
"main": "index.js",
"scripts": {
"dev": "webpack --mode development"
},
"author": "SJChen",
"license": "ISC",
"devDependencies": {
"webpack": "^4.29.5"
},
"dependencies": {}
}

執行指令dev以生成bundle.js

執行指令dev產生bundle.js

1
$ npm run dev

將index.html連結至bundle.js

index.html

1
2
3
<body>
<script src="js/bundle.js"></script>
</body>

Webpack-cli

安裝webpack-cli

1
$ npm install webpack-cli --save-dev

建立指令build

package.json

1
2
3
4
"scripts": {
"dev": "webpack --mode development",
"build": "webpack --mode production"
},

執行指令build

1
$ npm run build

Webpack-dev-server

安裝webpack-dev-server

1
$ npm install webpack-dev-server --save-dev

在config建立devServer

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
const path = require('path');

module.exports = {
entry: './src/js/index.js',
output: {
path: path.resolve(__dirname, 'dist/js'),
filename: 'bundle.js'
},
devServer: {
contentBase: './dist'
}
};

建立start指令

package.json

1
2
3
4
5
"scripts": {
"dev": "webpack --mode development",
"build": "webpack --mode production",
"start": "webpack-dev-server --mode development --open"
},

執行start以運行devServer

1
$ npm run start

HTML-Webpack-plugin

安裝html-webpack-plugin

1
$ npm install html-webpack-plugin --save-dev

在config建立plugins

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
entry: './src/js/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/bundle.js'
},
devServer: {
contentBase: './dist'
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/index.html'
})
]
};

執行start以生成index.html

1
$ npm run start

接下來應該是看到src/index.html的內容被輸入至index.html

註: 若是出錯可參考以下解法: 修改webpack.config.js裡的__dirname'dist',並將filename修改為'js/bundle.js'之後,再跑一次npm run start,就成功了。這是因為path要指向到dist裡的index.html,而非dist/js裡(這裡面沒有index.html)。

執行dev以同時生成html及js

1
$ npm run dev

在dist下出現index.html/js/bundle.js

Babel

安裝Babel相關套件

我們需要安裝三個套件:

  • babel-core
  • babel-preset-env
  • babel-loader
1
$ npm install babel-core babel-preset-env babel-loader --save-dev

在config建立module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');


module.exports = {
entry: './src/js/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/bundle.js'
},
devServer: {
contentBase: './dist'
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/index.html'
})
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]
}
};

新增.babelrc

在根目錄下建立.babelrc

1
2
3
4
5
6
7
8
9
10
11
12
{
"presets": [
["env", {
"targets": {
"browsers": [
"last 5 versions",
"ie >= 8"
]
}
}]
]
}

安裝Babel-polyfill

1
$ npm install babel-polyfill --save-dev

在config指定entry

1
2
3
4
5
6
7
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
entry: ['babel-polyfill', './src/js/index.js'],
output: {
//下略

回顧

資料夾結構


  • dist <–發佈版的資料夾
    • css
      • style.css
    • img
    • js
      • bundle.js <–輸出為ES5的js
    • index.html <–輸出結果
  • node_modules
  • src <–開發版的資料夾
    • js <–採用MVC結構
      • models
      • views
      • config.js <–一些不變的參數,例如API
      • index.js <–匯入models及views的controller
    • index.html
  • .babelrc
  • package-lock.json
  • package.json
  • webpack.config.js

.babelrc

1
2
3
4
5
6
7
8
9
10
11
12
{
"presets": [
["env", {
"targets": {
"browsers": [
"last 5 versions",
"ie >= 8"
]
}
}]
]
}

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');


module.exports = {
entry: ['babel-polyfill', './src/js/index.js'],
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/bundle.js'
},
devServer: {
contentBase: './dist'
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/index.html'
})
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]
}
};

閱讀全文

AngularJS與Webpack

引入Webpack整合開發環境

當AngularJS專案日益複雜,各component所使用的lib版本可能不一致(例如d3的v3,v4,v5),使用webpack可以避免全域變數被汙染,也可以對程式碼進行壓縮。

此外,將首頁中的script檔,由index.js統一管理,也可以避免因載入速度差異導致的undefined錯誤。

除了將js檔陸續移至index.js整合,也同時使用sass-loader,將原本的sass指令整合至webpack.config.js中。

需注意的是,webpack在整合js時,全域變數在瀏覽器中不再能單純的使用 var xxxfunction xxx 來定義,而須轉變成 window.xxx

閱讀全文

Sass Basic

安裝node-sass

1
2
3
4
$ npm init

$ npm install node-sass --save-dev
#--save-dev =>作為dev tool

建立資料夾scss 及 main.scss

創建main.scss

1
2
3
4
$ mkdir sass
$ cd sass/
$ touch main.scss

在package.json編寫compile腳本

開啟package.json, 在”scripts”那欄依序輸入: {"腳本名稱": "node-sass 來源檔 目的檔"}, 這是我們建立的腳本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"name": "mahoshojo",
"version": "1.0.0",
"description": "Landing page for mahoshojo",
"main": "index.js",
"scripts": {
"compile:sass": "node-sass app/sass/main.scss app/css/style.css"
//"腳本名稱": {"指令名稱": "node-sass 來源檔 目的檔"}
},

"author": "QB9487",
"license": "ISC",
"devDependencies": {
"node-sass": "^4.11.0"
},
"dependencies": {
"jquery": "^3.3.1"
}
}

在terminal執行腳本

1
2
3
4
5
6
7
$ npm run compile:sass
# 執行腳本
> mahoshojo@1.0.0 compile:sass D:\Sites\1-Mahoshojo
> node-sass app/sass/main.scss app/css/style.css

Rendering Complete, saving .css file...
Wrote CSS to D:\Sites\1-Mahoshojo\app\css\style.css

開啟 app\css\style.css, 可以看到main.scss的內容, 被轉譯後寫入style.css

1
2
3
4
5
6
7
8
.header {
height: 95vh;
background-image: linear-gradient(to right bottom, rgba(255, 153, 221, 0.7), rgba(150, 16, 94, 0.7)), url(../img/pink.jpg);
/* Photo by Sharon McCutcheon on Unsplash */
background-size: cover;
background-position: top;
position: relative;
clip-path: polygon(0 0, 100% 0, 100% 75vh, 0 100%); }

加入自動監看指令: -w

在腳本設定後加 -w 代表watch

1
2
3
"scripts": {
"compile:sass": "node-sass app/sass/main.scss app/css/style.css -w"
},

執行腳本時, 附加指令 -w, 每當main.scss存檔時, 腳本就會自動將其轉譯寫入style.css

1
2
3
4
5
6
7
8
9
10
$ npm run compile:sass -w
# 執行腳本 -w

> mahoshojo@1.0.0 compile:sass D:\Sites\1-Mahoshojo
> node-sass app/sass/main.scss app/css/style.css -w

=> changed: D:\Sites\1-Mahoshojo\app\sass\main.scss
Rendering Complete, saving .css file...
Wrote CSS to D:\Sites\1-Mahoshojo\app\css\style.css

註: 基本上這個功能和gulp watch是類似的, 但寫起來比gulp watch方便多了。

在全域安裝 Live server

1
2
3
4
5
6
7
8
9
$ npm install live-server -g
# -g 代表Globally(本機全域)

# 在LINUX/MAC系統可能要使用sudo權限
$ sudo npm install live-server -g

$ live-server -v
live-server 1.2.1
#查看Live-server的版本, 若有顯示則代表安裝成功

執行live-server

1
2
3
4
$ live-server
Serving "D:\Sites\1-Mahoshojo" at http://127.0.0.1:8080
Ready for changes
GET /favicon.ico 404 3.359 ms - 150

同時使用live-server及node-sass

目前我們能夠使用兩個package:

  • node-sass ==> 自動轉譯SCSS檔
  • live-server ==> 自動更新頁面

在有package.json的資料夾中執行node-sass:

1
$ npm run compile:sass -w

在有index.html的資料夾中執行live-server:

1
$ live-server

這兩者記得同時在兩個terminal開啟, 並且不要關掉, 其效果是, 每當我們儲存main.scss, 網頁就會自動更新成修改後的樣貌.

閱讀全文

Sass 與 WordPress

使用SCSS修改樣式

首先是用NPM安裝node-sass,再撰寫腳本指定覆蓋原本的style.css

../package.json

1
2
3
4
"scripts": {
"scss": "node-sass wordpress/wp-content/themes/sydney/scss/main.scss wordpress/wp-content/themes/sydney/style.css -w",

},

將原本的style.css改名為 _styleWP.scss,利用main.scss匯入後,再利用css權重去加入客製化樣式,如此一來就可以在舊有樣式上進行修改。

./scss/main.scss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@import "abstracts/mixin";
@import "abstracts/variables";
@import "abstracts/utility";

@import "base/base";
@import "base/styleWP";
@import "base/typography";

@import "components/archives";
@import "components/article";
@import "components/button";
@import "components/categories";
@import "components/navbar";
@import "components/search";
@import "components/footer";

@import "layout/home";
閱讀全文