Xin chào mọi người!
Chào mừng bạn đã đến với series/tutorial Lập trình Chrome extension với Typescript + Vue.js.
Trong bài hôm nay, chúng ta sẽ tiếp tục khắc phục các vấn đề khi build Chrome Extension trong bài trước.
First look
Bài trước, nếu bạn để ý thì sẽ thấy yarn serve
, yarn build
thực chất là script alias tới vue-cli-service
. Vue CLI Service cũng sử dụng webpack nhưng tại thời điểm hiện tại thì việc customize với webpack còn gặp một số vấn đề hạn chế:
- Multi entrypoints với webpack
- Config webpack theo document nhưng không nhận config webpack mới
Hy vọng trong thời gian tới chúng ta có thể tùy biến được tốt hơn.
Chúng ta sử dụng vue-cli-service
để thực hiện build Vue.js và TypeScript thành js “thuần” một cách rất nhanh chóng. Vậy thì thực sự, vue-cli-service
đã làm giúp chúng ta những gì? Dưới đây là một số thứ điển hình:
- Compile Vue.js + TypeScript và extract thành file
dist/js/app.[hash].js
- Compile SASS/CSS và extract thành file
dist/css/app.[hash].css
- Inject các file JavaScript và CSS sau khi build thành công vào file
dist/index.html
- Đóng gói các library dùng chung giữa các Vue component vào trong
chunk-vendors.[hash].js
. Ví dụ như: Vue.js, vue-class-component
Cài đặt dependency
Install Webpack
Chính bởi lẽ hạn chế ở trên nên mình sẽ dùng trực tiếp Webpack mà không còn dùng qua vue-cli-service như trước nữa. Chúng ta cần config một chút với webpack để thực hiện những việc mà vue-cli-service
đã làm ở trên. Đồng thời giải quyết các vấn đề đang gặp ở bài trước.
Bây giờ, hãy cùng mình cài đặt phiên bản mới nhất là v4.41.5. Và chúng ta sẽ vẫn tiếp tục cài đặt qua yarn
nhé:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | $ yarn add -D webpack webpack-cli yarn add v1.19.1 <span class="token punctuation">[</span>1/4<span class="token punctuation">]</span> Resolving packages<span class="token punctuation">..</span>. <span class="token punctuation">[</span>2/4<span class="token punctuation">]</span> Fetching packages<span class="token punctuation">..</span>. info <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a>: The platform <span class="token string">"linux"</span> is incompatible with this module. info <span class="token string">"<a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a>"</span> is an optional dependency and failed compatibility check. Excluding it from installation. info <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a>: The platform <span class="token string">"linux"</span> is incompatible with this module. info <span class="token string">"<a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a>"</span> is an optional dependency and failed compatibility check. Excluding it from installation. <span class="token punctuation">[</span>3/4<span class="token punctuation">]</span> Linking dependencies<span class="token punctuation">..</span>. <span class="token punctuation">[</span>4/4<span class="token punctuation">]</span> Building fresh packages<span class="token punctuation">..</span>. success Saved lockfile. success Saved 13 new dependencies. info Direct dependencies ├─ <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> └─ <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> Done <span class="token keyword">in</span> 4.80s. |
Kí tự
$
là ký tự nhắc dòng lệnh trên terminal của mình, vui lòng bỏ qua.
Install Loaders for webpack
Do không dùng qua vue-cli-service
nên mình sẽ cần cài đặt thêm một số loader để phục vụ việc compile một các file cần thiết như .vue|.ts|.ts|.css|.scss
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $ yarn add -D vue-loader ts-loader css-loader file-loader node-sass sass-loader yarn add v1.19.1 <span class="token punctuation">[</span>1/4<span class="token punctuation">]</span> Resolving packages<span class="token punctuation">..</span>. <span class="token punctuation">[</span>2/4<span class="token punctuation">]</span> Fetching packages<span class="token punctuation">..</span>. <span class="token punctuation">[</span>3/4<span class="token punctuation">]</span> Linking dependencies<span class="token punctuation">..</span>. <span class="token punctuation">[</span>4/4<span class="token punctuation">]</span> Building fresh packages<span class="token punctuation">..</span>. success Saved lockfile. info Direct dependencies ├─ <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> ├─ <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> ├─ <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> ├─ <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> ├─ <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> └─ <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> |
Trong đó:
vue-loader
: compile Vue.jsts-loader
: compile code TypeScriptcss-loader node-sass sass-loader
: phục vụ cho việc compile CSS, SASS.
Install Webpack Plugins
Tiếp tục, chúng ta cài đặt thêm webpack plugin cần thiết khác là mini-css-extract-plugin
, html-webpack-plugin
, copy-webpack-plugin
và cross-env
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $ yarn add -D mini-css-extract-plugin html-webpack-plugin copy-webpack-plugin cross-env yarn add v1.19.1 <span class="token punctuation">[</span>1/4<span class="token punctuation">]</span> Resolving packages<span class="token punctuation">..</span>. <span class="token punctuation">[</span>2/4<span class="token punctuation">]</span> Fetching packages<span class="token punctuation">..</span>. <span class="token punctuation">[</span>3/4<span class="token punctuation">]</span> Linking dependencies<span class="token punctuation">..</span>. <span class="token punctuation">[</span>4/4<span class="token punctuation">]</span> Building fresh packages<span class="token punctuation">..</span>. success Saved lockfile. success Saved 3 new dependencies. info Direct dependencies ├─ <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> ├─ <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> ├─ <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> └─ <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> |
Refactor code & folder structure
Sử dụng templates/popup.html
Do không dùng vue-cli-service
để build nên thư mục public cũng hơi thừa, mình sẽ chuyển public/index.html
tới templates/popup.html
, rồi sau đó xóa bỏ folder public
không còn cần thiết.
1 2 3 | <span class="token function">mv</span> public/index.html templates/popup.html <span class="token function">rm</span> -rf public |
Xóa bỏ thẻ link không dùng trong templates/popup.html
, cuối cùng nội dung sẽ như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token doctype"><!DOCTYPE html></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>utf-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>X-UA-Compatible<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>IE=edge<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>width=device-width,initial-scale=1.0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Prodwarn<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>noscript</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>strong</span><span class="token punctuation">></span></span>We're sorry but Prodwarn doesn't work properly without JavaScript enabled. Please enable it to continue.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>strong</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>noscript</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>app<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> <span class="token comment"><!-- built files will be auto injected --></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span> |
Update NPM scripts
Các lệnh build
, serve
cũng được chuyển qua dùng webpack:
1 2 3 4 5 6 7 8 9 | <span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"serve"</span><span class="token operator">:</span> <span class="token string">"cross-env NODE_ENV=development webpack -w --hide-modules"</span><span class="token punctuation">,</span> <span class="token property">"build"</span><span class="token operator">:</span> <span class="token string">"cross-env NODE_ENV=production webpack --hide-modules"</span><span class="token punctuation">,</span> <span class="token property">"lint"</span><span class="token operator">:</span> <span class="token string">"vue-cli-service lint"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"dependencies"</span><span class="token operator">:</span> <span class="token punctuation">{</span> ... <span class="token punctuation">}</span> |
Trong đó:
webpack -w
sẽ thêm file watcher, giúp tự build lại source code mỗi khi file source code bị sửa đổicross-env
giúp mình define environment variable nhưNODE_ENV
Bây giờ lệnh serve
không còn serve web server để chúng ta truy cập qua http://localhost:8080 như trước nữa, mà chỉ là tạo watcher để tự build lại code. Do đó, mình sẽ đổi command alias từ serve
thành start
.
1 2 | <span class="token property">"start"</span><span class="token operator">:</span> <span class="token string">"cross-env NODE_ENV=development webpack -w --hide-modules"</span><span class="token punctuation">,</span> |
Refactor folder structure
Vì thông thường, một browser extension có thể có nhiều thành phần chứ không chỉ riêng popup. Để gọn gàng hơn một chút, chúng ta sẽ làm gọn gàng hơn một chút giống Viblo Browser Extension đó là chuyển code của popup vào một folder riêng tên là src/popup
. Nhân tiện, chúng ta tạo sẵn một folder src/content-scripts
với một file main.ts
ở trong với nội dung:
1 2 3 | <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'content-scripts are called! ^^'</span><span class="token punctuation">)</span> |
Cấu trúc thư mục src
bây giờ sẽ trở thành:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $ tree src/ src/ ├── content-scripts │ └── main.ts └── popup ├── App.vue ├── assets │ └── logo.png ├── components │ └── HelloWorld.vue ├── main.ts ├── shims-tsx.d.ts └── shims-vue.d.ts 4 directories, 7 files |
Cấu hình webpack
Tạo file webpack.config.js
Như bạn đã thấy, chúng ta sẽ setup webpack để compile code cho hai thành phần trong extension gồm:
src/content-scripts
src/popup
Chúng ta cần compile chúng thành 2 file riêng biệt, do đó, mình sẽ cần tạo 2 entrypoint cho webpack. Rất tiếc, webpack chỉ support 1 entrypoint mà thôi. Do đó chúng ta phải config webpack riêng biệt cho từng cái.
1 2 3 4 5 6 7 8 9 | <span class="token keyword">const</span> contentScripts <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./webpack/content-scripts'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> popup <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./webpack/popup'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">[</span> contentScripts<span class="token punctuation">,</span> popup<span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> |
Config webpack cho content-scripts
MÌnh sẽ có một bài tiếp theo về các thành phần trong browser extension, cũng như chức năng, tác dụng của chúng để mọi người hiểu nên trong bài này bạn hãy tạm hiểu content-scripts
sẽ là các script của mình để thực hiện thao tác với DOM của một trang web bạn đang truy cập. Ví dụ, khi chúng ta vào trang Viblo và muốn đổi background của trang từ trắng sang đen, code xử lý đó sẽ được đặt trong content-scripts
và khai báo trong manifest.json
để browser thực thi.
Content-scripts của mình cũng khá đơn giản vì chỉ là code TypeScript, dó đó, chúng ta chỉ cần sử dụng babel-loader
và ts-loader
trong webpack là đủ. File cấu hình sẽ như sau:
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 | <span class="token keyword">const</span> <span class="token punctuation">{</span> resolve <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'path'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span> mode<span class="token punctuation">:</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">'content-scripts'</span><span class="token punctuation">,</span> entry<span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token string">'./src/content-scripts/main.ts'</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> output<span class="token punctuation">:</span> <span class="token punctuation">{</span> path<span class="token punctuation">:</span> <span class="token function">resolve</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">'../dist'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> filename<span class="token punctuation">:</span> <span class="token string">'content-scripts/main.js'</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> resolve<span class="token punctuation">:</span> <span class="token punctuation">{</span> extensions<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">'.js'</span><span class="token punctuation">,</span> <span class="token string">'.ts'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> alias<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token string">'@'</span><span class="token punctuation">:</span> <span class="token function">resolve</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">'../src'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> module<span class="token punctuation">:</span> <span class="token punctuation">{</span> rules<span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> test<span class="token punctuation">:</span> <span class="token regex">/.js$/</span><span class="token punctuation">,</span> loader<span class="token punctuation">:</span> <span class="token string">'babel-loader'</span><span class="token punctuation">,</span> exclude<span class="token punctuation">:</span> <span class="token regex">/node_modules/</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> test<span class="token punctuation">:</span> <span class="token regex">/.ts$/</span><span class="token punctuation">,</span> loader<span class="token punctuation">:</span> <span class="token string">'ts-loader'</span><span class="token punctuation">,</span> exclude<span class="token punctuation">:</span> <span class="token regex">/node_modules/</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> |
Tạm thời chúng ta có thể comment dòng số 2 và 6 trong webpack.config.js
như này để build thử:
1 2 3 4 5 6 7 8 9 | <span class="token keyword">const</span> contentScripts <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./webpack/content-scripts'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// const popup = require('./webpack/popup');</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">[</span> contentScripts<span class="token punctuation">,</span> <span class="token comment">// popup,</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | $ yarn build yarn run v1.19.1 $ cross-env NODE_ENV<span class="token operator">=</span>production webpack --hide-modules Hash: 44bfd3d4e4a2b06c1184 Version: webpack 4.41.5 Child content-scripts: Hash: 44bfd3d4e4a2b06c1184 Time: 796ms Built at: 01/08/2020 12:23:16 AM Asset Size Chunks Chunk Names content-scripts.js 1020 bytes 0 <span class="token punctuation">[</span>emitted<span class="token punctuation">]</span> main Entrypoint main <span class="token operator">=</span> content-scripts.js Done <span class="token keyword">in</span> 1.79s. |
Config webpack cho Popup
Popup thì config nhiều hơn một chút vì nó phải làm khá nhiều thứ với Vue.js, TypeScript, Images, SASS.
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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | <span class="token keyword">const</span> <span class="token punctuation">{</span> resolve <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'path'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> HtmlPlugin <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'html-webpack-plugin'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// eslint-disable-line</span> <span class="token keyword">const</span> MiniCssExtractPlugin <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'mini-css-extract-plugin'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// eslint-disable-line</span> <span class="token keyword">const</span> VueLoaderPlugin <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'vue-loader/lib/plugin'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// eslint-disable-line</span> <span class="token keyword">const</span> devMode <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span> <span class="token operator">!==</span> <span class="token string">'production'</span><span class="token punctuation">;</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span> mode<span class="token punctuation">:</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">'popup'</span><span class="token punctuation">,</span> entry<span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token string">'./src/popup/main.ts'</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> output<span class="token punctuation">:</span> <span class="token punctuation">{</span> path<span class="token punctuation">:</span> <span class="token function">resolve</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">'../dist/popup'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> filename<span class="token punctuation">:</span> <span class="token string">'js/index.js'</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> resolve<span class="token punctuation">:</span> <span class="token punctuation">{</span> extensions<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">'.js'</span><span class="token punctuation">,</span> <span class="token string">'.ts'</span><span class="token punctuation">,</span> <span class="token string">'.vue'</span><span class="token punctuation">,</span> <span class="token string">'.scss'</span><span class="token punctuation">,</span> <span class="token string">'.css'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> alias<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token string">'@'</span><span class="token punctuation">:</span> <span class="token function">resolve</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">'../src'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> module<span class="token punctuation">:</span> <span class="token punctuation">{</span> rules<span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> test<span class="token punctuation">:</span> <span class="token regex">/.js$/</span><span class="token punctuation">,</span> loader<span class="token punctuation">:</span> <span class="token string">'babel-loader'</span><span class="token punctuation">,</span> exclude<span class="token punctuation">:</span> <span class="token regex">/node_modules/</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> test<span class="token punctuation">:</span> <span class="token regex">/.ts$/</span><span class="token punctuation">,</span> loader<span class="token punctuation">:</span> <span class="token string">'ts-loader'</span><span class="token punctuation">,</span> exclude<span class="token punctuation">:</span> <span class="token regex">/node_modules/</span><span class="token punctuation">,</span> options<span class="token punctuation">:</span> <span class="token punctuation">{</span> appendTsSuffixTo<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token regex">/.vue$/</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> test<span class="token punctuation">:</span> <span class="token regex">/.vue$/</span><span class="token punctuation">,</span> loader<span class="token punctuation">:</span> <span class="token string">'vue-loader'</span><span class="token punctuation">,</span> options<span class="token punctuation">:</span> <span class="token punctuation">{</span> loaders<span class="token punctuation">:</span> <span class="token punctuation">{</span> scss<span class="token punctuation">:</span> <span class="token string">'vue-style-loader!css-loader!sass-loader'</span><span class="token punctuation">,</span> sass<span class="token punctuation">:</span> <span class="token string">'vue-style-loader!css-loader!sass-loader?indentedSyntax'</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> test<span class="token punctuation">:</span> <span class="token regex">/.scss$/</span><span class="token punctuation">,</span> use<span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> loader<span class="token punctuation">:</span> MiniCssExtractPlugin<span class="token punctuation">.</span>loader <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token string">'css-loader'</span><span class="token punctuation">,</span> <span class="token string">'sass-loader'</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> test<span class="token punctuation">:</span> <span class="token regex">/.(eot|ttf|woff|woff2)(?S*)?$/</span><span class="token punctuation">,</span> loader<span class="token punctuation">:</span> <span class="token string">'file-loader'</span><span class="token punctuation">,</span> esModule<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> test<span class="token punctuation">:</span> <span class="token regex">/.(png|jpg|svg|gif|svg)$/</span><span class="token punctuation">,</span> loader<span class="token punctuation">:</span> <span class="token string">'file-loader'</span><span class="token punctuation">,</span> options<span class="token punctuation">:</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">'img/[name].[ext]?[hash]'</span><span class="token punctuation">,</span> esModule<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> plugins<span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token keyword">new</span> <span class="token class-name">VueLoaderPlugin</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">MiniCssExtractPlugin</span><span class="token punctuation">(</span><span class="token punctuation">{</span> filename<span class="token punctuation">:</span> devMode <span class="token operator">?</span> <span class="token string">'css/[name].css'</span> <span class="token punctuation">:</span> <span class="token string">'css/[name].[hash].css'</span><span class="token punctuation">,</span> chunkFilename<span class="token punctuation">:</span> devMode <span class="token operator">?</span> <span class="token string">'css/[id].css'</span> <span class="token punctuation">:</span> <span class="token string">'css/[id].[hash].css'</span><span class="token punctuation">,</span> ignoreOrder<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">HtmlPlugin</span><span class="token punctuation">(</span><span class="token punctuation">{</span> template<span class="token punctuation">:</span> <span class="token function">resolve</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">'../templates/popup.html'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> filename<span class="token punctuation">:</span> <span class="token string">'index.html'</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> |
Trong đó, mình sử dụng MiniCssExtractPlugin
để extract css thành file .css
, HtmlPlugin
để generate file /popup/index.html
(đã inject cả /popup/js/index.js
và /popup/css/main.css
.
Bỏ comment trong webpack.config.js
để chúng ta chạy webpack build thử cả popup
và content-scripts
:
1 2 3 4 5 6 7 8 9 | <span class="token keyword">const</span> contentScripts <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./webpack/content-scripts'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> popup <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./webpack/popup'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">[</span> contentScripts<span class="token punctuation">,</span> popup<span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> |
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 | $ yarn build yarn run v1.15.2 $ cross-env NODE_ENV<span class="token operator">=</span>production webpack --hide-modules Hash: 2ac2f2827d5103f38c5c0ea7ef83af28f19be4fd Version: webpack 4.41.5 Child content-scripts: Hash: 2ac2f2827d5103f38c5c Time: 2773ms Built at: 01/09/2020 12:51:09 PM Asset Size Chunks Chunk Names content-scripts.js 1020 bytes 0 <span class="token punctuation">[</span>emitted<span class="token punctuation">]</span> main Entrypoint main <span class="token operator">=</span> content-scripts.js Child popup: Hash: 0ea7ef83af28f19be4fd Time: 4421ms Built at: 01/09/2020 12:51:11 PM Asset Size Chunks Chunk Names css/main.0ea7ef83af28f19be4fd.css 368 bytes 0 <span class="token punctuation">[</span>emitted<span class="token punctuation">]</span> <span class="token punctuation">[</span>immutable<span class="token punctuation">]</span> main img/logo.png?82b9c7a5a3f405032b1db71a25f67021 6.69 KiB <span class="token punctuation">[</span>emitted<span class="token punctuation">]</span> index.html 619 bytes <span class="token punctuation">[</span>emitted<span class="token punctuation">]</span> js/index.js 76.6 KiB 0 <span class="token punctuation">[</span>emitted<span class="token punctuation">]</span> main Entrypoint main <span class="token operator">=</span> css/main.0ea7ef83af28f19be4fd.css js/index.js Child html-webpack-plugin <span class="token keyword">for</span> <span class="token string">"index.html"</span><span class="token keyword">:</span> 1 asset Entrypoint undefined <span class="token operator">=</span> index.html Child mini-css-extract-plugin node_modules/css-loader/dist/cjs.js<span class="token operator">!</span>node_modules/vue-loader/lib/loaders/stylePostLoader.js<span class="token operator">!</span>node_modules/sass-loader/dist/cjs.js<span class="token operator">!</span>node_modules/vue-loader/lib/index.js??vue-loader-options<span class="token operator">!</span>src/popup/App.vue?vue<span class="token operator">&</span>type<span class="token operator">=</span>style<span class="token operator">&</span>index<span class="token operator">=</span>0<span class="token operator">&</span>lang<span class="token operator">=</span>scss<span class="token operator">&</span><span class="token keyword">:</span> Entrypoint mini-css-extract-plugin <span class="token operator">=</span> * Child mini-css-extract-plugin node_modules/css-loader/dist/cjs.js<span class="token operator">!</span>node_modules/vue-loader/lib/loaders/stylePostLoader.js<span class="token operator">!</span>node_modules/sass-loader/dist/cjs.js<span class="token operator">!</span>node_modules/vue-loader/lib/index.js??vue-loader-options<span class="token operator">!</span>src/popup/components/HelloWorld.vue?vue<span class="token operator">&</span>type<span class="token operator">=</span>style<span class="token operator">&</span>index<span class="token operator">=</span>0<span class="token operator">&</span>id<span class="token operator">=</span>20a26824<span class="token operator">&</span>scoped<span class="token operator">=</span>true<span class="token operator">&</span>lang<span class="token operator">=</span>scss<span class="token operator">&</span><span class="token keyword">:</span> Entrypoint mini-css-extract-plugin <span class="token operator">=</span> * Done <span class="token keyword">in</span> 5.25s. |
Như vậy là đã thành công rồi!
Tự động tạo manifest.json và ảnh logo
Tới thời điểm này, còn một vấn đề mà chúng ta chưa giải quyết đó là việc tự động copy file manifest.json
, static
vào dist
sau mỗi lần build source code.
Lần này, chúng ta sẽ dùng copy-webpack-plugin
để cấu hình tự động generate manifest.json
và copy thư mục static
mỗi khi build.
Nếu bạn để ý, hai file config webpack sẽ có ouput folder khác nhau. Với popup
thì sẽ là dist/popup
còn content-scripts
sẽ là dist
. Vì các file manifest.json
và static
cần copy vào root, nên config webpack lần này mình sẽ thêm vào webpack/content-scripts
.
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 | <span class="token keyword">const</span> <span class="token punctuation">{</span> resolve <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'path'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> CopyWebpackPlugin <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'copy-webpack-plugin'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// eslint-disable-line</span> <span class="token keyword">const</span> pkg <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'../package.json'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> manifestTemplate <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'../templates/manifest.json'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token operator">...</span> module<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> plugins<span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token keyword">new</span> <span class="token class-name">CopyWebpackPlugin</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token punctuation">{</span> <span class="token keyword">from</span><span class="token punctuation">:</span> <span class="token string">'./templates/manifest.json'</span><span class="token punctuation">,</span> to<span class="token punctuation">:</span> <span class="token string">'manifest.json'</span><span class="token punctuation">,</span> transform<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> manifestTemplate<span class="token punctuation">.</span>version <span class="token operator">=</span> pkg<span class="token punctuation">.</span>version<span class="token punctuation">;</span> <span class="token keyword">return</span> Buffer<span class="token punctuation">.</span><span class="token keyword">from</span><span class="token punctuation">(</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>manifestTemplate<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token keyword">from</span><span class="token punctuation">:</span> <span class="token string">'./static'</span><span class="token punctuation">,</span> to<span class="token punctuation">:</span> <span class="token string">'static'</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> |
Đoạn code trên sử dụng chức năng transform
của copy-webpack-plugin
, giúp mình chèn tên version
mới tại mỗi lần release. Chúng ta chỉ cần return Buffer
trong hàm transform
là được, còn version
mình sẽ lấy từ file package.json
cho tiện release.
Thử build lại lần cuối:
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 | $ yarn build yarn run v1.15.2 $ cross-env NODE_ENV<span class="token operator">=</span>production webpack --hide-modules Hash: 2ac2f2827d5103f38c5c0ea7ef83af28f19be4fd Version: webpack 4.41.5 Child content-scripts: Hash: 2ac2f2827d5103f38c5c Time: 2104ms Built at: 01/09/2020 1:02:12 PM Asset Size Chunks Chunk Names content-scripts/main.js 1020 bytes 0 <span class="token punctuation">[</span>emitted<span class="token punctuation">]</span> main manifest.json 445 bytes <span class="token punctuation">[</span>emitted<span class="token punctuation">]</span> static/images/logo-128.png 3.19 KiB <span class="token punctuation">[</span>emitted<span class="token punctuation">]</span> static/images/logo-16.png 513 bytes <span class="token punctuation">[</span>emitted<span class="token punctuation">]</span> static/images/logo-32.png 1.07 KiB <span class="token punctuation">[</span>emitted<span class="token punctuation">]</span> static/images/logo-48.png 1.36 KiB <span class="token punctuation">[</span>emitted<span class="token punctuation">]</span> Entrypoint main <span class="token operator">=</span> content-scripts/main.js Child popup: Hash: 0ea7ef83af28f19be4fd Time: 2638ms Built at: 01/09/2020 1:02:12 PM Asset Size Chunks Chunk Names css/main.0ea7ef83af28f19be4fd.css 368 bytes 0 <span class="token punctuation">[</span>emitted<span class="token punctuation">]</span> <span class="token punctuation">[</span>immutable<span class="token punctuation">]</span> main img/logo.png?82b9c7a5a3f405032b1db71a25f67021 6.69 KiB <span class="token punctuation">[</span>emitted<span class="token punctuation">]</span> index.html 619 bytes <span class="token punctuation">[</span>emitted<span class="token punctuation">]</span> js/index.js 76.6 KiB 0 <span class="token punctuation">[</span>emitted<span class="token punctuation">]</span> main Entrypoint main <span class="token operator">=</span> css/main.0ea7ef83af28f19be4fd.css js/index.js Child html-webpack-plugin <span class="token keyword">for</span> <span class="token string">"index.html"</span><span class="token keyword">:</span> 1 asset Entrypoint undefined <span class="token operator">=</span> index.html Child mini-css-extract-plugin node_modules/css-loader/dist/cjs.js<span class="token operator">!</span>node_modules/vue-loader/lib/loaders/stylePostLoader.js<span class="token operator">!</span>node_modules/sass-loader/dist/cjs.js<span class="token operator">!</span>node_modules/vue-loader/lib/index.js??vue-loader-options<span class="token operator">!</span>src/popup/App.vue?vue<span class="token operator">&</span>type<span class="token operator">=</span>style<span class="token operator">&</span>index<span class="token operator">=</span>0<span class="token operator">&</span>lang<span class="token operator">=</span>scss<span class="token operator">&</span><span class="token keyword">:</span> Entrypoint mini-css-extract-plugin <span class="token operator">=</span> * Child mini-css-extract-plugin node_modules/css-loader/dist/cjs.js<span class="token operator">!</span>node_modules/vue-loader/lib/loaders/stylePostLoader.js<span class="token operator">!</span>node_modules/sass-loader/dist/cjs.js<span class="token operator">!</span>node_modules/vue-loader/lib/index.js??vue-loader-options<span class="token operator">!</span>src/popup/components/HelloWorld.vue?vue<span class="token operator">&</span>type<span class="token operator">=</span>style<span class="token operator">&</span>index<span class="token operator">=</span>0<span class="token operator">&</span>id<span class="token operator">=</span>20a26824<span class="token operator">&</span>scoped<span class="token operator">=</span>true<span class="token operator">&</span>lang<span class="token operator">=</span>scss<span class="token operator">&</span><span class="token keyword">:</span> Entrypoint mini-css-extract-plugin <span class="token operator">=</span> * Done <span class="token keyword">in</span> 3.84s. |
Bạn thấy trong log build sẽ có cả manifest.json
, static/images/logo-*.png
được thêm thành công. Tuy nhiên, khi mở extension thì thấy báo lỗi File Not Found
, nguyên nhân do chúng ta đã chuyển file index.html
vào trong popup/index
nên cấu hình file manifest.json
trở thành bị lỗi. Hãy đổi link default_popup: "popup/index.html"
rồi build lại là được.
Vậy là các vấn đề đã được giải quyết. Bạn có thể và mở lại extension và vậy thấy nó hoạt động bình thường. Nếu bạn đang follow theo tut này mà gặp vấn đề nào đó, hãy để lại comment để mình cùng thảo luận nhé. Chúc bạn thành công và hẹn gặp lại ở phần sau!
TL;DR
Link tham khảo:
- VueLoader Plugin for webpack
- HTML Webpack Plugin
- Mini CSS Webpack Plugin
- Copy Webpack Plugin
- Full source code – Lesson 02
Nếu bạn thấy series này hay và hữu ích thì đừng quên share, upvote, folow mình để đón đọc các bài viết tiếp theo của mình trên Viblo nhé!
Bạn cũng có thể subscribe các tag về Vue và Typescript trên Viblo để nhận được nhiều hơn nữa các bài viết mới nhất về các topic này trên Vbilo nhé! Viblo sử dụng các dữ liệu mà bạn folow, quan tâm cho hệ thống gợi ý nên các bạn hãy folow những topic bạn yêu thích để hệ thống recommend thêm nhiều bài hay và bổ ích từ hàng nghìn bài viết trên Viblo mà bạn còn chưa từng đọc!