Solid

196 阅读3分钟

生命周期

onMount

onMount(() => {
    console.log('onMount exec========')
})

onCleanup

onCleanup(() => {
    console.log('onCleanup exec=========')
})

响应式

Solid 响应性原理大致上是将任何响应性计算封装在函数中,并在其依赖关系更新时重新运行该函数。Solid JSX 编译器还用一个函数包装了大多数 JSX 表达式(括号中的代码),因此当依赖关系发生变化时,它们会自动更新(并触发相应的 DOM 更新)。更准确地说,每当函数在跟踪范围内被调用时,就会自动重新运行该函数,例如 JSX 表达式或构建 “计算” 的 API 调用(createEffect, createMemo 等)。默认情况下,在跟踪范围内调用函数时,通过检测函数何时读取反应状态(例如,通过 signal getter 或 Store 属性),自动跟踪函数的依赖关系。因此,您通常不需要担心依赖关系。(但是,如果自动依赖项跟踪无法产生您想要的结果,您可以 覆盖依赖项跟踪)这种方法使响应性可组合:在另一个函数中调用一个函数通常会导致调用函数继承被调用函数的依赖关系。

路由

@solidjs/router

npm install @solidjs/router

配置式路由

import { lazy } from "solid-js";
import { render } from "solid-js/web";
import { Router } from "@solidjs/router";

const routes = [
  {
    path: "/users",
    component: lazy(() => import("/pages/users.js")),
  },
  {
    path: "/users/:id",
    component: lazy(() => import("/pages/users/[id].js")),
    children: [
      {
        path: "/",
        component: lazy(() => import("/pages/users/[id]/index.js")),
      },
      {
        path: "/settings",
        component: lazy(() => import("/pages/users/[id]/settings.js")),
      },
      {
        path: "/*all",
        component: lazy(() => import("/pages/users/[id]/[...all].js")),
      },
    ],
  },
  {
    path: "/",
    component: lazy(() => import("/pages/index.js")),
  },
  {
    path: "/*all",
    component: lazy(() => import("/pages/[...all].js")),
  },
];

render(() =>
  <Router base="/admin">{routes}</Router>,
  document.getElementById("app")
);

路由跳转

import { useNavigate } from '@solidjs/router'

const navigate = useNavigate()

navigate('/users', {state : {...}, replace: false})

因为navigate方法无法添加url参数(如:/users?name=123),所以我们要对路由跳转进行封装,于是便有了以下代码:

router目录下新建hooks.ts文件用于提供路由相关工具方法。

import { useNavigate } from '@solidjs/router'

export function navigatePush(path, options){
    const navigate = useNavigate()    navigate(path, options)
}

在home组件中调用navigatePush方法

function Home(){
    const handleClick(){
        navigatePush('/list' , {'name':123})
    }

}

此时发现报错

Uncaught Error: Make sure your app is wrapped in a <Router />
    at invariant (utils.js:29:15)
    at useRouter (routing.js:8:32)
    at useNavigate (routing.js:22:34)
    at redirectTo (hooks.ts:49:19)
    at HTMLButtonElement.golist [as $$click] (index.tsx:71:3)
    at HTMLDocument.eventHandler (dev.js:859:66)

正确解决方案:利用函数闭包保存navigate,在跳转时执行navigate方法。

import { useNavigate } from '@solidjs/router'
interface RouterOptions {
    replace?: boolean
    scroll?: boolean
    resolve?: boolean
    state?: any
    params?: any
}

// 根据给定的对象params,生成url参数key1=value1&key2=value2
export function generateUrlParams(params: any) {
    let urlParams = ''
    if (!params) return urlParams
    urlParams = Object.keys(params)
        .map((key) => {
            let value = ''
            if (typeof params[key] === 'object') {
                try {
                    value = JSON.stringify(params[key])
                } catch (error) {
                    console.log(error)
                }
            } else {
                value = params[key]
            }
            return `${key}=${value}`
        })
        .join('&')
    return urlParams
}

export function useRouteNavigatePush() {
    const navigate = useNavigate()
    return (path: string, options?: RouterOptions) => {
        if (!path) return
        const { replace = false, scroll = true, resolve = true, state, params } = options || {}
        const routeOptions: any = { replace, scroll, resolve }
        if (state) {
            routeOptions.state = state
        }
        const urlParams = generateUrlParams(params)
        navigate(`${path}${urlParams && urlParams.length > 0 ? '?' : ''}${urlParams}`, routeOptions)
    }
}

在home组件中调用useRouteNavigatePush方法

import { useRouteNavigatePush} from '../../router/hooks'

function Home() {
    const navigatePush = useRouteNavigatePush()

    const golist = () => {
        navigatePush('/list', { state: { a: 123, b: 'kkkkk' }, params: { suuid: '123' } })
    }
    return (<div onClick={golist}>go list</div>)
}

状态管理

我们可以建立不同的ts文件,通过createStore来管理不同模块的状态。

todos.ts:

import { createStore } from 'solid-js/store'

const [state, setState] = createStore({
    todos: [
        { task: 'Finish work', completed: false },
        { task: 'Go grocery shopping', completed: false },
        { task: 'Make dinner', completed: false }
    ]
})

export function getTodos() {
    return state.todos
}

export function setTodos(params = {}) {
    setState('todos', (old) => {
        return [...old, ...params]
    })
}

users.ts:

import { createStore } from 'solid-js/store'

const [state, setState] = createStore({
    users: {
        tel: '13912345678',
        name: 'john',
        age: 22
    }
})

export function getUsers() {
    return state.users
}

export function setUsers(params = {}) {
    setState('users', (old) => {
        return { ...old, ...params }
    })
}

png图片路径引用

import receive from '../../assets/images/receive.png'

<img src={receive} />

svg使用

1、直接img标签引入

import solidsvg from '../../assets/svg/solid.svg'

<img src={solidsvg} />

2、svg标签引入

样式

less的基本使用

以less为例,我们先安装less。

npm install -D less

组件样式文件名改为index.module.less,采用模块化的样式防止不同组件样式冲突。

导入样式文件并使用:

1、直接引入

2、动态判断

import styles from './index.module.less'

return (
    <>
        <div class={styles.homeArea}>
            ...
        </div>
       <div classList={{ [styles.homeArea]: isShowArea }}>
            ...
        </div>
    </>
)

less中导入自定义全局样式报错

我们经常会在assets目录下定义自己的全局样式,比如assets/styles/vars.less。

在react+webpack项目中,我们经常这样写:

@import '~@/assets/styles/vars.less';

vite+solid项目中直接这样写会报错,修改如下:

1、tsconfig.json中compilerOptions添加paths

"compilerOptions": {
    "paths": {
            "@/*": ["./src/*"]
    }
}

2、vite.config.ts中添加配置

## 先安装path
npm install --save --dev @types/node


## 在vite.config.js中,配置
import { defineConfig } from 'vite'
import solid from 'vite-plugin-solid'
import { resolve } from 'path'
export default defineConfig({
  plugins: [solid()],
  resolve: {
    alias: {
      '@': resolve(__dirname, './src')
    }
  },
  css: {
    preprocessorOptions: {
      less: {
        additionalData: `@import '${resolve('./src/assets/styles/vars.less')}';`
      }
    }
  }
})

3、导入路径修改

@import '@/assets/styles/vars.less';

关闭类型强检测

默认情况下,vite build编译会报一堆类型检测错误,我们可以修改tsconfig.json参数暂时避免报错:

"compilerOptions": {
     "noImplicitAny": false // 关闭第三方包declaration的强检测
}

参考:

solidjs官方文档

SolidJS 学习

webpack2 + vue2 老项目迁移 vite 成功! 是真香啊