What is Snowpack?

Take a look at the official website:

Snowpack is a lightning-fast frontend build tool, designed for the modern web. It is an alternative to heavier, more complex bundlers like webpack or Parcel in your development workflow. Snowpack leverages JavaScript’s native module system (known as ESM) to avoid unnecessary work and stay fast no matter how big your project grows.

Once you try it, it’s impossible to go back to anything else.


Snowpack is a lightning-fast front-end build tool designed for the modern Web. It is an alternative to heavier, more complex packagers such as Webpack or Parcel in the development workflow. Snowpack leverages JavaScript’s native module system (called ESM) to avoid unnecessary work and stay fast no matter how big your project is.

Once you’ve tried it, it’s impossible to go back to anything else.

Snowpack: Fast!

In this paper, the target

  • Build react project based on Snowpack, including routing, UI and other functions;
  • Extensions to projects such as typescript, less, ESLint, stylelint, prettier;
  • Configuration around the project, such as environment variables, path aliases, package paths, and so on.

\

Create the Snowpack project

Initialize the

Create the Snowpack project with the following command.

npx create-snowpack-app react-snowpack --template @snowpack/app-template-minimal
Copy the code

You can run the following two commands to access the directory and start the system.

cd react-snowpack
npm run start
Copy the code

After running, you can see the project Snowpack started.

Open this project and you’ll find it’s empty. Don’t worry, let’s add React to it.

React’s first page

Run the following command to install.

npm install react react-dom react-router-dom --save
Copy the code

Place the React logo under SRC/Assets /images.

Create a folder SRC under the root directory. Inside SRC, create app. JSX. This will be our first page. App. JSX will look like this.

import React, { useState, useEffect } from "react"; import logo from "./assets/images/logo.png"; import "./App.css"; function App() { const [count, setCount] = useState(0); useEffect(() => { const timer = setTimeout(() => setCount(count + 1), 1000); return () => clearTimeout(timer); }, [count, setCount]); return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1>Hello snowpack! </h1> <p> Page has been open for <code>{count}</code> seconds. </p> </header> </div> ); } export default App;Copy the code

Create SRC/app.css and add the following:

.App { text-align: center; }.App p {margin: 0.4rem; } .App-logo { height: 40vmin; } @media (prefers-reduced-motion: no-preference) { .App-logo { animation: App-logo-spin infinite 20s linear; } } .App-header { background-color: #282c34; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: calc(10px + 2vmin); color: white; } @keyframes App-logo-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); }}Copy the code

Open the index.html file and add an empty

tag to mark where you want React to display the content.
<div id="root"></div>
Copy the code

Js file in the root directory and create index.jsx to render the React Dom.

import React from "react";
import ReactDOM from "react-dom";
import APP from "./src/App";
ReactDOM.render(
  	<React.StrictMode>
    		<APP />
  	</React.StrictMode>,
  	document.getElementById("root")
);
Copy the code

After waiting for the page to refresh automatically, you can see the first page we created.

Add the routing

Routing is essential in a single-page project, so let’s add routing.

Respectively created SRC/pages/hello/index. The JSX files and SRC/pages/snowpack/index. The JSX file, as routing jump test page. As follows:

// hello/index.jsx import React from "react"; function Hello() { return <div className="App">I'm Hello, How are u? </div>; } export default Hello;Copy the code
// snowpack/index.jsx import React from "react"; function Snowpack() { return <div className="App">I'm Snowpack, I'm fine thank you, and you? </div>; } export default Snowpack;Copy the code

Move app.jsx and app.css to SRC /home and change the file name, app.jsx -> index.jsx, app.css -> index.css, and change the component name of index.jsx to home.

Recreate app.jsx under SRC to place the route content.

import React from "react";
import { HashRouter, Switch, Route } from "react-router-dom";
import Index from "./pages/home/index.jsx";
import Hello from "./pages/hello/index.jsx";
import Snowpack from "./pages/snowpack/index.jsx";

function App() {
  	return (
    		<div className="index">
            <HashRouter>
                <Switch>
                    <Route exact path="/" component={Index} />
                    <Route exact path="/hello" component={Hello} />
                    <Route exact path="/snowpack" component={Snowpack} />
                </Switch>
            </HashRouter>
    		</div>
  	);
}
Copy the code

Add a route forward in the pages/home/index directory.

import React, { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import logo from '.. /.. /assets/images/logo.svg'; import './index.css'; function Home() { const [count, setCount] = useState(0); useEffect(() => { const timer = setTimeout(() => setCount(count + 1), 1000); return () => clearTimeout(timer); }, [count, setCount]); return ( <div className="App"> <header className="App-header"> <nav> <ul> <li> <Link to="/hello">hello</Link> </li> <li>  <Link to="/snowpack">snowpack</Link> </li> </ul> </nav> <img src={logo} className="App-logo" alt="logo" /> <h1>Hello snowpack! </h1> <p> Page has been open for <code>{count}</code> seconds. </p> </header> </div> ); } export default Home;Copy the code

Now there is a jump link in the page, click the link to realize the jump!

Jump effect:

Antd

Install the following packages, @ant-Design/ICONS for antD Icon dependency libraries and dayjs for ANTD internal component dependency libraries.

npm install antd @ant-design/icons dayjs --save 
Copy the code

Then import “antd/dist/antd.css” in app.jsx; When you need CSS, you can use the related components directly.

Engineering configuration

With the basic project in place, let’s start engineering the configuration.

Less

Install less.

npm install less --save-dev
Copy the code

Install snowpack-plugin-less so that Snowpack can recognize less and convert it to CSS.

npm install snowpack-plugin-less --save-dev
Copy the code

Then add the snowpack-plugin-less plugin to snowpack.config.mjs.

// snowpack.config.mjs
export default {
  	plugins: ['snowpack-plugin-less']
}
Copy the code

Eslint

Install eslint.

npm install eslint --save-dev
Copy the code

After installing the ESLint package, run esLint –init, as shown below for my installation options.

The.eslintrc file is automatically created in the project after installation. If you use hooks, you need to install the eslint-plugin-react-hooks package to implement hooks rules for ESLint.

The following is the recommended configuration:

module.exports = {
    extends: [
        'eslint:recommended',
        'plugin:react/recommended',
        'plugin:react-hooks/recommended',
        'standard',
        'prettier',
    ],
    env: {
        browser: true,
        commonjs: true,
        es6: true
    },
    parser: '@typescript-eslint/parser',
    parserOptions: {
        ecmaFeatures: {
            jsx: true,
            modules: true
        },
        sourceType: 'module',
        ecmaVersion: 6
    },
    plugins: ['react', '@typescript-eslint'],
    settings: {
        'import/ignore': ['node_modules'],
        react: {
            version: 'latest'
        }
    },
    rules: {
        quotes: [2, 'single'],
        'no-console': 0,
        'no-debugger': 1,
        'no-var': 1,
        semi: ['error', 'always'],
        'no-irregular-whitespace': 0,
        'no-trailing-spaces': 1,
        'eol-last': 0,
        'no-unused-vars': [
            1,
            {
                vars: 'all',
                args: 'after-used'
            }
        ],
        'no-prototype-builtins': 0,
        'no-case-declarations': 0,
        'no-underscore-dangle': 0,
        'no-alert': 2,
        'no-lone-blocks': 0,
        'no-class-assign': 2,
        'no-cond-assign': 2,
        'no-const-assign': 2,
        'no-delete-var': 2,
        'no-dupe-keys': 2,
        'use-isnan': 2,
        'no-duplicate-case': 2,
        'no-dupe-args': 2,
        'no-empty': 2,
        'no-func-assign': 2,
        'no-invalid-this': 0,
        'no-redeclare': 2,
        'no-spaced-func': 2,
        'no-this-before-super': 0,
        'no-undef': 2,
        'no-return-assign': 0,
        'no-script-url': 2,
        'no-use-before-define': 0,
        'no-extra-boolean-cast': 0,
        'no-unreachable': 1,
        'comma-dangle': 2,
        'no-mixed-spaces-and-tabs': 2,
        'prefer-arrow-callback': 0,
        'arrow-parens': 0,
        'arrow-spacing': 0,
        camelcase: 0,
        'jsx-quotes': [1, 'prefer-double'],
        'react/display-name': 0,
        'react/forbid-prop-types': [
            2,
            {
                forbid: ['any']
            }
        ],
        'react/jsx-boolean-value': 0,
        'react/jsx-closing-bracket-location': 1,
        'react/jsx-curly-spacing': [
            2,
            {
                when: 'never',
                children: true
            }
        ],
        'react/jsx-indent': ['error', 4],
        'react/jsx-key': 2,
        'react/jsx-no-bind': 0,
        'react/jsx-no-duplicate-props': 2,
        'react/jsx-no-literals': 0,
        'react/jsx-no-undef': 1,
        'react/jsx-pascal-case': 0,
        'react/jsx-sort-props': 0,
        'react/jsx-uses-react': 1,
        'react/jsx-uses-vars': 2,
        'react/no-danger': 0,
        'react/no-did-mount-set-state': 0,
        'react/no-did-update-set-state': 0,
        'react/no-direct-mutation-state': 2,
        'react/no-multi-comp': 0,
        'react/no-set-state': 0,
        'react/no-unknown-property': 2,
        'react/prefer-es6-class': 2,
        'react/prop-types': 0,
        'react/react-in-jsx-scope': 0,
        'react/self-closing-comp': 0,
        'react/sort-comp': 0,
        'react/no-array-index-key': 0,
        'react/no-deprecated': 1,
        'react/jsx-equals-spacing': 2
    }
};
Copy the code

After the successful configuration, many files have become red, is it possible that some students have made obsessive-compulsive disorder, want to manually modify these problems? Don’t worry, we’ll install Prettier later to format code with one click.

Stylelint

Eslint also has a natural stylelint. Eslint checks js code formatting, while stylelint checks CSS code formatting.

Install the Stylelint-related package first, and then create stylelint.config.js in your project to store the stylelint-related rules.

npm install stylelint stylelint-config-standard --save-dev 
echo {}> stylelint.config.js
Copy the code

The following is the recommended configuration:

module.exports = {
    extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
    ignoreFiles: [
        '**/*.ts',
        '**/*.tsx',
        '**/*.png',
        '**/*.jpg',
        '**/*.jpeg',
        '**/*.gif',
        '**/*.mp3',
        '**/*.json'
    ],
    rules: {
        'at-rule-no-unknown': [
            true,
            {
                ignoreAtRules: ['extends', 'ignores']
            }
        ],
        indentation: 4,
        'number-leading-zero': null,
        'unit-allowed-list': ['em', 'rem', 's', 'px', 'deg', 'all', 'vh', 'vw', '%'],
        'no-eol-whitespace': [
            true,
            {
                ignore: 'empty-lines'
            }
        ],
        'declaration-block-trailing-semicolon': 'always',
        'selector-pseudo-class-no-unknown': [
            true,
            {
                ignorePseudoClasses: ['global']
            }
        ],
        'block-closing-brace-newline-after': 'always',
        'declaration-block-semicolon-newline-after': 'always',
        'no-descending-specificity': null,
        'selector-list-comma-newline-after': 'always',
        'selector-pseudo-element-colon-notation': 'single'
    }
};
Copy the code

Prettier

Next comes Prettier, a must-have for every front-end development, because instead of formatting code manually, it helps prettier format code with one click.

Install the prettier,

npm install prettier  --save-dev
Copy the code

Because esLint is used in the project, install eslint-config-Prettier so that ESLint can work with Prettier by turning off esLint rules that contradict Prettier.

Then use echo {}> prettier. Config. js to create the prettier configuration file.

The following is the recommended configuration:

Module.exports = {// Max 100 characters printWidth: 100, // use 4 Spaces to indent tabWidth: 4, // use useTabs instead of indentation: False, // A semicolon should be used at the end of the line, // singleQuote: true, // The key of the object should be quoted only when necessary: 'as-needed', // JSX uses double quotes instead of single quotes, // trailingComma: 'None ', // braces need spacing at the beginning and end of bracketSpacing: true, // JSX tag Angle brackets need newline jsxBracketSameLine: False, // arrowParens: 'avoid', // Each file is formatted to the full contents of the file rangeStart: 0, rangeEnd: // @prettier requirePragma: false does not automatically insert @prettier insertPragma: False, / / use the default fold line standard proseWrap: 'preserve, / / according to display style decided to don't fold line htmlWhitespaceSensitivity HTML: 'CSS ', // use lf endOfLine: 'lf'};Copy the code

Then run the following command from the command line to format the entire file.

npx prettier --write .
Copy the code

Prettier-code formatter if you want to save format in vscode with one click, install the prettier-code formatter plug-in and modify the vscode configuration according to the tutorial.

Typescript

Install typescript and react and react-dom type definitions with the following command.

npm install typescript --save-dev
npm install @types/react --save-dev
npm install @types/react-dom --save-dev
npm install @typescript-eslint/eslint-plugin --save-dev
npm install @typescript-eslint/parser --save-dev
Copy the code

If you need package detection at compile time, you also need to install the following packages.

npm install @snowpack/plugin-typescript --save-dev
Copy the code

And configure it in plugins in snowpack.config.mjs:

// snowpack.config.mjs
export default {
  	plugins: ['@snowpack/plugin-typescript']
}
Copy the code

Create tsconfig.json and configure it by echo {}> tsconfig.json.

The following is the recommended configuration:

{
    "compilerOptions": {
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "target": "ESNext",
        "allowSyntheticDefaultImports": true,
        "strict": true,
        "forceConsistentCasingInFileNames": true,
        "allowJs": true,
        "outDir": "./dist/",
        "esModuleInterop": true,
        "noImplicitAny": false,
        "sourceMap": true,
        "module": "esnext",
        "moduleResolution": "node",
        "isolatedModules": true,
        "importHelpers": true,
        "lib": ["esnext", "dom", "dom.iterable"],
        "skipLibCheck": false,
        "jsx": "react",
        "baseUrl": "./src",
        "paths": {
              "@src/*": ["*"],
              "@assets/*": ["assets/*"],
              "@components/*": ["components/*"],
              "@pages/*": ["pages/*"],
              "@utils/*": ["utils/*"],
              "@servers/*": ["servers/*"],
              "@actions/*": ["actions/*"],
              "@config": ["config"],
              "@routeConfig": ["routeConfig"],
              "@request": ["request"]
          }
    },
    "include": ["./src/**/*", "./declaration.d.ts"],
    "exclude": ["node_modules"]
}
Copy the code

In tsconfig.json, paths are the aliases supported by TS. In addition to tsconfig.json, paths need to be configured in snowpack.config. MJS so that snowpack can be converted into corresponding paths. For project configuration, see snowpack.config. MJS Related Configuration – Path Alias Configuration section.

pre-commit

Install HusKY and List-staged.

npm install husky -save-dev
npm install lint-staged -save-dev
Copy the code

Add some configuration to package.json.

"scripts": { ... , "lint:jsx": "eslint --ext .jsx,.js src", "lint:css": "stylelint --aei .less .css src", "precommit": "lint-staged", "precommit-msg": "echo 'Pre-commit checks... ' && exit 0" }, "husky": { "hooks": { "pre-commit": "npm run lint-staged" } }, "lint-staged": { "*.{js,jsx,ts,tsx}": [ "eslint --fix", "prettier --write" ], "*.{css,less}": [ "stylelint --fix", "prettier --write" ] }Copy the code

Snowpack.config. MJS related configuration

Package path configuration

First of all, we will organize the project’s public resources file. In general, we will put the project’s index.html and index.css as static resources under /public. As shown below:

After moving, make sure to change the import path of CSS and JS in index.html.

After following the steps above, the basic structure and content of the project have been configured. Now let’s run NPM run build from the command line to see how the packaged file structure looks.

When you expand build, you can see that the files in public are packed into the build/public file, but normally you would expect index.html to be in the build root. You can mount public to the build root directory using the following configuration.

// snowpack.config.mjs
export default {
  	mount: {
				public: '/',
		}
}
Copy the code

Besides, the packaged files are not only our business code, but also the configuration code of the project, such as package.json, perttier.config.js, readme.md, etc. This code is unnecessary and takes up a small amount of space. You can package only files in the SRC directory with the following configuration Settings.

// snowpack.config.mjs
export default {
  	mount: {
				src: '/src',
		}
}
Copy the code

Package the output directory configuration

// snowpack.config.mjs
export default {
  	out: 'dist'
};
Copy the code

Path Alias Configuration

Alias allows you to configure path abbreviations and imported aliases for Node packages.

// snowpack.config. MJS export default {alias: {// Type 1: node package import alias lodash: 'lodash-es', // Type 2: Local file into the alias' @ SRC ':'. / SRC ', '@ assets' :'. / SRC/assets', '@ components' :'. / SRC/components', '@ pages: './src/pages', '@utils': './src/utils/', '@servers': './src/servers', '@actions': './src/actions', '@config': './src/config.ts', '@routeConfig': './src/routeConfig.tsx', '@request': './src/request.ts' }, };Copy the code

Packaging environment differentiation

Usually when we package, we want to distinguish the environment of the package by packaging command. The first step is to add the environment values to the corresponding script commands in the scripts configuration of package.json.

"scripts": {
    "start": "NODE_ENV=development snowpack dev",
    "build:test": "NODE_ENV=test snowpack build",
    "build:prod": "NODE_ENV=production snowpack build",
},
Copy the code

If you need to obtain the environment from snowpack.config. MJS, you can obtain it directly from process.env.node_env as follows:

// snowpack.config.mjs export default { plugins: (() => { const plugin = ['snowpack-plugin-less']; if (process.env.NODE_ENV === 'development') { plugin.push('@snowpack/plugin-typescript'); } return plugin; }}) (),Copy the code

NODE_ENV in import.meta. Env, as follows:

const { NODE_ENV } = import.meta.env;
Copy the code

Setting global variables

We also need to set some built-in global variables to be used by HTML and js files, such as snowpack.config. MJS env configuration:

// snowpack.config.mjs
export default {
  	env: {
        ENVIRONMENT: 'test'
    },
}
Copy the code

Import. Meta. Env: import.

const { ENVIRONMENT } = import.meta.env;
Copy the code

In HTML, you can use %ENVIRONMENT% directly:

<script>
  	console.log('%ENVIRONMENT%');
</script>
Copy the code

In DevTools, you can see that part changed to the following.

Questions & Precautions

  1. Snowpack runtime Uncaught ReferenceError: Require is not defined.

The error was reported because Snowpack only supports ESM standard code and cannot parse CommonJS.

  1. snowpack Unable to resolve cnpmInstall the package, so after the installation of the package startup error, or found that the package does not take effect, you can deletenode_moduleswithnpmyarnThe installation.
  2. NPM start error
[18:30:15] [esinstall:xmlhttprequest-ssl] /Users/huxiaomiao/pivos/react-snowpack3/node_modules/xmlhttprequest-ssl/lib/XMLHttpRequest.js Module "fs" (Node.js built-in) is not available in the browser. Run Snowpack with --polyfill-node to fix. [18:30:15] [esinstall:xmlhttprequest-ssl] /Users/huxiaomiao/pivos/react-snowpack3/node_modules/xmlhttprequest-ssl/lib/XMLHttpRequest.js Module "url" (Node.js built-in) is not available in the browser. Run Snowpack with --polyfill-node to fix. [18:30:15] [esinstall:xmlhttprequest-ssl] /Users/huxiaomiao/pivos/react-snowpack3/node_modules/xmlhttprequest-ssl/lib/XMLHttpRequest.js Module "https" (Node.js built-in) is not available in the browser. Run Snowpack with --polyfill-node to fix. [18:30:15] [esinstall:xmlhttprequest-ssl] /Users/huxiaomiao/pivos/react-snowpack3/node_modules/xmlhttprequest-ssl/lib/XMLHttpRequest.js Module "child_process" (Node.js built-in) is not available in the browser. Run Snowpack with --polyfill-node to fix. [18:30:15] [esinstall:xmlhttprequest-ssl] /Users/huxiaomiao/pivos/react-snowpack3/node_modules/xmlhttprequest-ssl/lib/XMLHttpRequest.js Module "url" (Node.js built-in) is not available in the browser. Run Snowpack with --polyfill-node to fix. [18:30:15] [esinstall:xmlhttprequest-ssl] /Users/huxiaomiao/pivos/react-snowpack3/node_modules/xmlhttprequest-ssl/lib/XMLHttpRequest.js Module "fs" (Node.js built-in) is not available in the browser. Run Snowpack with --polyfill-node to fix. [18:30:15] [esinstall:xmlhttprequest-ssl] /Users/huxiaomiao/pivos/react-snowpack3/node_modules/xmlhttprequest-ssl/lib/XMLHttpRequest.js Module "http" (Node.js built-in) is not available in the browser. Run Snowpack with --polyfill-node to fix. [18:30:15] [esinstall:xmlhttprequest-ssl] /Users/huxiaomiao/pivos/react-snowpack3/node_modules/xmlhttprequest-ssl/lib/XMLHttpRequest.js Module "http" (Node.js built-in) is not available in the browser. Run Snowpack with --polyfill-node to fix. [18:30:15] [esinstall:xmlhttprequest-ssl] /Users/huxiaomiao/pivos/react-snowpack3/node_modules/xmlhttprequest-ssl/lib/XMLHttpRequest.js Module "https" (Node.js built-in) is not available in the browser. Run Snowpack with --polyfill-node to fix. [18:30:15] [esinstall:xmlhttprequest-ssl] /Users/huxiaomiao/pivos/react-snowpack3/node_modules/xmlhttprequest-ssl/lib/XMLHttpRequest.js Module "child_process" (Node.js built-in) is not available in the browser. Run Snowpack with --polyfill-node to fix. [18:30:15] [esinstall:xmlhttprequest-ssl] url? commonjs-external Module "url" (Node.js built-in) is not available in the browser. Run Snowpack with --polyfill-node to fix. [18:30:15] [esinstall:xmlhttprequest-ssl] https? commonjs-external Module "https" (Node.js built-in) is not available in the browser. Run Snowpack with --polyfill-node to fix. [18:30:15] [esinstall:xmlhttprequest-ssl] http? commonjs-external Module "http" (Node.js built-in) is not available in the browser. Run Snowpack with --polyfill-node to  fix. [18:30:15] [esinstall:xmlhttprequest-ssl] fs? commonjs-external Module "fs" (Node.js built-in) is not available in the browser. Run Snowpack with --polyfill-node to fix. [18:30:15] [esinstall:xmlhttprequest-ssl] child_process? commonjs-external Module "child_process" (Node.js built-in) is not available in the browser. Run Snowpack with --polyfill-node to fix. [18:30:15] [snowpack] Install failed for xmlhttprequest-ssl.Copy the code

Solution:

www.snowpack.dev/reference/c…

The last

After these, a basic project framework is basically completed, friends go to practice, welcome to discuss any questions in the comments section.

Here is my simple project framework, want to experience friends can directly clone down to play oh ~

Github.com/mogiihu/sno…